53

Below is a snippet from the book C Programming Just the FAQs. Isn't this wrong as Arrays can never be passed by value?

VIII.6: How can you pass an array to a function by value?

Answer: An array can be passed to a function by value by declaring in the called function the array name with square brackets ([ and ]) attached to the end. When calling the function, simply pass the address of the array (that is, the array’s name) to the called function. For instance, the following program passes the array x[] to the function named byval_func() by value:

The int[] parameter tells the compiler that the byval_func() function will take one argument—an array of integers. When the byval_func() function is called, you pass the address of the array to byval_func():

byval_func(x);

Because the array is being passed by value, an exact copy of the array is made and placed on the stack. The called function then receives this copy of the array and can print it. Because the array passed to byval_func() is a copy of the original array, modifying the array within the byval_func() function has no effect on the original array.

Zuzu
  • 3,363
  • 5
  • 27
  • 16
  • 2
    This is true for structs - you can pass them by value (over the stack) or by reference but not for arrays. – sinelaw Jan 23 '11 at 15:07
  • 19
    This is in a book about C, really? I see it dates from 1995, but this was already plain wrong then. – Jens Gustedt Jan 23 '11 at 16:11
  • 31
    If that is what it says in the book, you need to chuck the book away - it contains serious misinformation on a basic issue, and who knows what other misinformation on what other issues. The last quoted paragraph is nonsense - if you modify the array in the called function, you are modifying the array in the calling function too, because arrays are not passed by value in C. – Jonathan Leffler Jan 23 '11 at 16:17
  • 1
    Plainly wrong.. – not-a-user Oct 19 '17 at 15:09
  • "*When the `byval_func()` function is called, you pass the address of the array to `byval_func()`:*" - This is the only half-correct sentence from that quote. I wonder why not one of the 5 authors nor at least one of the 7 proofreaders encountered that sentence and evaluated it right. – RobertS supports Monica Cellio Mar 09 '20 at 14:02

6 Answers6

78

Because the array is being passed by value, an exact copy of the array is made and placed on the stack.

This is incorrect: the array itself is not being copied, only a copy of the pointer to its address is passed to the callee (placed on the stack). (Regardless of whether you declare the parameter as int[] or int*, it decays into a pointer.) This allows you to modify the contents of the array from within the called function. Thus, this

Because the array passed to byval_func() is a copy of the original array, modifying the array within the byval_func() function has no effect on the original array.

is plain wrong (kudos to @Jonathan Leffler for his comment below). However, reassigning the pointer inside the function will not change the pointer to the original array outside the function.

Péter Török
  • 114,404
  • 31
  • 268
  • 329
  • He indeed is. In C++ you can of course have array objects passed by value, but in C, an array is just a pointer. – Stefan H Singer Jan 23 '11 at 15:07
  • 5
    StefanHållén: You can't pass (or return) arrays by value in either C or C++, you can pass arrays by reference in C++ because C++ has reference types. – CB Bailey Jan 23 '11 at 15:09
  • I meant that you could use for instance std::array. Instances of that can be passed and returned by value. – Stefan H Singer Jan 23 '11 at 15:11
  • @StefanHållén: Please see the tag, this is a C question. – CB Bailey Jan 23 '11 at 15:25
  • I just mentioned it as a, heh, reference. – Stefan H Singer Jan 23 '11 at 15:27
  • 5
    I would go further; although the sentence you quote is mildly ambiguous and could charitably be construed as correct, the last sentence is simply wrong within the normal meaning of the term 'changing the array'. If the called function writes to `array[0]`, it modifies `array[0]` in the calling function - under the obvious call notation. – Jonathan Leffler Jan 23 '11 at 16:25
  • @Jonathan, excellent point, thanks. I updated my answer accordingly. – Péter Török Jan 23 '11 at 16:52
  • @StefanHSinger An array is not a pointer and a pointer is not an array. An array passed into a function by reference decays to a pointer to the first element. Just thought I would clarify that. – Mushy Sep 23 '17 at 16:59
65

Burn that book. If you want a real C FAQ that wasn't written by a beginner programmer, use this one: http://c-faq.com/aryptr/index.html.

Syntax-wise, strictly speaking you cannot pass an array by value in C.

void func (int* x); /* this is a pointer */

void func (int x[]); /* this is a pointer */

void func (int x[10]); /* this is a pointer */

However, for the record there is a dirty trick in C that does allow you to pass an array by value in C. Don't try this at home! Because despite this trick, there is still never a reason to pass an array by value.

typedef struct
{
  int my_array[10];
} Array_by_val;

void func (Array_by_val x);
Lundin
  • 195,001
  • 40
  • 254
  • 396
  • 6
    Cosmetics for 3rd example means you can do a `sizeof(array)/sizeof(array[0])` in the function – Ulterior Jun 29 '12 at 03:17
  • 5
    @Ulterior Wouldn't that be nice if it worked? But alas you are wrong, the C standard isn't that smart. Try this: `void func (int arr[10]) { printf("No, I am still a pointer, my size is %d.", sizeof(arr)); }`. It will print 4 or 8 on a PC, not 40 as you would expect. – Lundin Dec 11 '12 at 10:01
  • 4
    "Because despite this trick, there is still never a reason to pass an array by value." -- why? –  Jan 23 '13 at 10:50
  • 1
    @pbs Lets say you have a 32-bit system and an array of 10 ints. You can pass a pointer, occupy 4 bytes of memory and have the program run fast, or you can use the above dirty trick, occupy 40 bytes of memory and have the program run slow. Furthermore, if you pass an array by value you can't alter it so that the caller is aware of the change. – Lundin Jan 24 '13 at 17:47
  • 5
    @Lundin I agree that this is better if it can be done. But what if an algorithm requires different copies of the array at each level of a tree. I have written an algorithm in which a copy of a number of points is required for every node of a tree called, and there is no avoiding the amount of memory used, for otherwise the points would not be distinct, which is required. The only possible drawback is that it might be faster in such cases to allocate all memory in one go at the beginning... Then again if the required data is dynamic this may not be possible in general. –  Jan 25 '13 at 13:20
  • @pbs Why wouldn't it be possible to declare each array locally then? And when would there ever be a point of passing it on by value? – Lundin Jan 26 '13 at 19:12
  • No, in all three cases `a` is of type `int*`; it's not an array pointer. – Keith Thompson Jun 10 '13 at 06:37
  • @KeithThompson That's correct, I don't know where I got the array pointer idea from. I'll remove that parenthesis from the post since it is creating confusion. – Lundin Jun 10 '13 at 13:13
  • 1
    I think this is the biggest misconception about C to date. I wish the pass by array gave a warning to educate those unaware; it's such a big cause of argument, as some self proclaimed experts, especially school professors won't go out of their way to test it, and make people hate C and programming in general. You can see this misconception in every piece of code that has `int main(int argc, char *argv[])`, since argv isnt an array of zero or more items, it's an address of the first item(supposedly, it doesn't even have to be)! – Dmytro Aug 09 '17 at 22:03
2

Isn't this wrong as arrays can never be passed by value?

Exactly. You cannot pass an array by value in C.

I took a look at the quoted part of the book and the source of this confusion or mistake is pretty fast found.

The author did not know about that *i is equivalent to i[] when provided as a parameter to a function. The latter form was invented to explicitly illustrate the reader of the code, that i points to an array, which is a great source of confusion, as well-shown by this question.

What I think is funny, that the author of the particular part of the book or at least one of the other parts (because the book has 5 authors in total) or one of the 7 proofreaders did not mentioned at least the sentence:

"When the byval_func() function is called, you pass the address of the array to byval_func():"

With at least that, they should had noticed that there is a conflict. Since you passing an address, it is only an address. There is nothing magically happen which turns an address into a whole new array.


But back to the question itself:

You can not pass an array as it is by value in C, as you already seem to know yourself. But you can do three (there might be more, but that is my acutal status of it) things, which might be an alternative depending on the unique case, so let´s start.

  1. Encapsulate an array in a structure (as mentioned by other answers):
#include <stdio.h>

struct a_s {
   int a[20];
};

void foo (struct a_s a)
{
   size_t length = sizeof a.a / sizeof *a.a;

   for(size_t i = 0; i < length; i++)
   {
       printf("%d\n",a.a[i]);
   }
}

int main()
{
   struct a_s array;

   size_t length = sizeof array.a / sizeof *array.a;

   for(size_t i = 0; i < length; i++)
   {
       array.a[i] = 15;
   } 

   foo(array);
}
  1. Pass by pointer but also add a parameter for determine the size of the array. In the called function there is made a new array with that size information and assigned with the values from the array in the caller:
#include <stdio.h>

void foo (int *array, size_t length)
{
   int b[length];

   for(size_t i = 0; i < length; i++)
   {
       b[i] = array[i];
       printf("%d\n",b[i]);
   }
}

int main()
{
   int a[10] = {0,1,2,3,4,5,6,7,8,9};

   foo(a,(sizeof a / sizeof *a));
}
  1. Avoid to define local arrays and just use one array with global scope:
#include <stdio.h>

int a[10];
size_t length = sizeof a / sizeof *a;

void foo (void)
{
   for(size_t i = 0; i < length; i++)
   {
       printf("%d\n",a[i]);
   }
}

int main()
{   
   for(size_t i = 0; i < length; i++)
   {
       a[i] = 25;
   } 

   foo();
}
1

In C and C++ it is NOT possible to pass a complete block of memory by value as a parameter to a function, but we are allowed to pass its address. In practice this has almost the same effect and it is a much faster and more efficient operation.

To be safe, you can pass the array size or put const qualifier before the pointer to make sure the callee won't change it.

Rongkai Xu
  • 67
  • 1
  • 1
    > To be safe, you can pass the array size or put const qualifier before the pointer to make sure the callee won't change it... WRONG!The function can still modify the memory, even with a `const` qualifier, although the compiler may try to make it hard. The following compiles without warnings on `gcc -Wall`. ``` void func(const int* ptr) { *(int*)(&ptr[0]) = 123; } #include int main() { int arr[2] = {1, 2}; printf("arr[0]: %d\n", arr[0]); func(arr); printf("arr[0]: %d\n", arr[0]); return 0; } ``` will print ``` arr[0]: 1 arr[0]: 123 ``` – oromoiluig Jan 03 '19 at 19:33
0

Yuo can work it around by wrapping the array into the struct

#include <stdint.h>
#include <stdio.h>

struct wrap
{
    int x[1000];
};

struct wrap foo(struct wrap x)
{
    struct wrap y;

    for(int index = 0; index < 1000; index ++)
        y.x[index] = x.x[index] * x.x[index];
    return y;
}

int main ()
{
    struct wrap y;

    for(int index = 0; index < 1000; index ++)
        y.x[index] = rand();
    y = foo(y);
    for(int index = 0; index < 1000; index ++)
    {
        printf("%d %s", y.x[index], !(index % 30) ? "\n" : "");
    }


}
0___________
  • 60,014
  • 4
  • 34
  • 74
-15
#include<stdio.h>
void  fun(int a[],int n);
int main()
{
    int a[5]={1,2,3,4,5};
    fun(a,5);
}
void  fun(int a[],int n)
{
    int i;
    for(i=0;i<=n-1;i++)
        printf("value=%d\n",a[i]);
}

By this method we can pass the array by value, but actually the array is accessing through the its base address which actually copying in the stack.

Rob
  • 4,927
  • 12
  • 49
  • 54
Varun Chhangani
  • 1,116
  • 3
  • 13
  • 18
  • 2
    No, that does *not* pass the array by value. The parameter is a pointer. – Keith Thompson Jun 10 '13 at 06:34
  • And if `fun` changed the value of `a[0]`, that change would be visible in `main`. – Keith Thompson Jun 10 '13 at 14:49
  • so how we pass the array by value. In this there are simple passing array and array also behave like constant pointer so when you will pass array by value it's simply means you are passing it by pointer. If you have any example please tell me. – Varun Chhangani Jun 11 '13 at 06:03
  • You cannot directly pass an array as a parameter in C. (You can pass a structure that has an array as a member, but that's not all that useful, since the array then has to have a fixed size known at compile time.) Suggested reading: Section 6 of the [comp.lang.c FAQ](http://www.c-faq.com). – Keith Thompson Jun 11 '13 at 06:36
  • 1
    The accepted answer is correct; array is passed by reference through a pointer to the first item in the array. Primitives are passed by value to the function. – Mushy Sep 23 '17 at 16:55
  • To see for yourself, set a[2] to 9 in fun() and printf it in main() after the fun() call. – AstroFloyd Mar 24 '19 at 08:17