Hello I was reading this question, I got confused with how if we can pass arrays by value or not. Here is a piece of code which I think should pass array by value.
#include <cstdio>
void foo (int arr[]);
int main()
{
int arr[10];
foo(arr[10]);
return 0;
}
void foo (int arr[])
{
.......
}
Please tell me why wouldn't it pass by value?
Thanks
Arrays automatically decay into pointers in certain contexts in C. Function calls are one of those places. That said, you are passing the pointer by value - C has no other way to pass parameters than "by value".
Because this:
void foo(int arr[])
is really just syntax sugar for this:
void foo(int *arr)
So when you call the function, your array decays into a pointer to the first element.
In short: you cannot pass an array as a function parameter and have it received as an array type by the called function. An array expression in that context will be converted to a pointer type before it is passed to the function.
From the C language standard:
6.3.2.1 Lvalues, arrays, and function designators
...
3 Except when it is the operand of the sizeof operator or the unary & operator, or is a
string literal used to initialize an array, an expression that has type ‘‘array of type’’ is
converted to an expression with type ‘‘pointer to type’’ that points to the initial element of
the array object and is not an lvalue. If the array object has register storage class, the
behavior is undefined.
What this means is that when you write something like
int arr[10];
foo(arr);
the expression arr in the call to foo is immediately converted to a pointer type; it is said to "decay" to type int *. So all foo will ever receive is a pointer value.
More standard language:
6.7.5.3 Function declarators (including prototypes)
...
7 A declaration of a parameter as ‘‘array of type’’ shall be adjusted to ‘‘qualified pointer to
type’’, where the type qualifiers (if any) are those specified within the [ and ] of the
array type derivation. If the keyword static also appears within the [ and ] of the
array type derivation, then for each call to the function, the value of the corresponding
actual argument shall provide access to the first element of an array with at least as many
elements as specified by the size expression.
What this means is that if your prototype for the function foo is either
void foo(int arr[])
or
void foo(int arr[10])
it will be interpreted as
void foo(int *arr)
Again, the definition of the language is such that you cannot pass an array expression as a function parameter and have it be received as an array type by the called function.
There are ways around this; you can wrap the array in a struct and pass the struct by value, like so:
struct T { int arr[10]; } var;
foo(var);
...
void foo (struct T arg) { ... }
As hacks go, this doesn't really buy you much.
Related
The following C program:
int doStuff(int afm[]);
int main(){
int afm1[9] = {1,2,3,4,5,6,7,8,9}; //size=9
int afmLength = sizeof(afm1)/sizeof(int);
printf("main: Length Of Array=%d\n", afmLength); //9 OK
int k = doStuff(afm1);
system("PAUSE");
return 0;
}
int doStuff(int afm[]){
int afmLength = sizeof(afm)/sizeof(int);
printf("doStuff: Length Of Array=%d\n", afmLength); //1 WRONG
return 1;
}
produces the following output:
main: Length Of Array=9
doStuff: Length Of Array=1
Why is the array size calculated correctly in main, but is wrong inside the function?
Because in main you have an array and in the function you have a pointer to that array.
int doStuff(int afm[])
is equivalent to
int doStuff(int *afm)
Adding to David Heffernan's answer (which is correct), you should have another parameter which would be the array length passed onto your doStuff method.
From the C language standard (draft n1256):
6.3.2.1 Lvalues, arrays, and function designators
...
3 Except when it is the operand of the sizeof operator or the unary & operator, or is a
string literal used to initialize an array, an expression that has type ‘‘array of type’’ is
converted to an expression with type ‘‘pointer to type’’ that points to the initial element of
the array object and is not an lvalue. If the array object has register storage class, the
behavior is undefined.
Memorize that paragraph, since one of the biggest sources of heartburn in C programming is how C treats array expressions.
When you call doStuff(afm1);, the expression afm1 is implicitly converted from type "9-element array of int" to "pointer to int", and the expression's value is the same as &afm1[0]. So what doStuff receives is a pointer value, not an array.
In the context of a function parameter declaration, T a[] and T a[N] are both interpreted as T *a:
6.7.5.3 Function declarators (including prototypes)
...
7 A declaration of a parameter as ‘‘array of type’’ shall be adjusted to ‘‘qualified pointer to
type’’, where the type qualifiers (if any) are those specified within the [ and ] of the
array type derivation. If the keyword static also appears within the [ and ] of the
array type derivation, then for each call to the function, the value of the corresponding
actual argument shall provide access to the first element of an array with at least as many
elements as specified by the size expression.
Since doStuff receives a pointer value and not an array, the sizeof trick doesn't work. In general, you have to explicitly tell a function how large of an array you're passing to it; you can't determine that from the pointer value itself.
So, when you call doStuff from main, you'll need to do something like
doStuff(afm1, sizeof afm1/sizeof *afm1);
...
int doStuff(int *afm, size_t afmsize)
{
...
}
What is the difference between the below codes when passing the array to the function? Which should be used when writing a code?
PS: I would like to know the difference in using int *ptr vs int ptr[] in the function argument.
void foo(int *ptr)
{
...
...
}
void main()
{
int a[]={10,20,30};
foo(a);
}
vs
void foo(int ptr[])
{
...
...
}
void main()
{
int a[]={10,20,30};
foo(a);
}
They're exactly the same.
If a parameter to a function is declared to have array type, that parameter is adjusted to pointer type.
This is spelled out in section 6.7.6.3p7 of the C standard regarding Function Declarators:
A declaration of a parameter as ‘‘array of type’’ shall be adjusted to
‘‘qualified pointer to type’’, where the type qualifiers (if any) are
those specified within the [ and ] of the array type derivation. If
the keyword static also appears within the [ and ] of the array type
derivation, then for each call to the function, the value of the
corresponding actual argument shall provide access to the first
element of an array with at least as many elements as specified by the
size expression.
This is in part due to the fact that in most contexts an array decays to a pointer to its first element, so it's not even possible to pass an array to a function (although you can pass a pointer to an array).
From a readability standpoint, you might want to use the array syntax when you expect to receive a pointer to the first element of the array, while you might want to use the pointer syntax when you expect to receive a pointer to a single object.
For example:
void foo1(int *p)
{
*p += 2;
}
void foo2(int p[], int len)
{
int i;
for (i=0; i<len; i++) {
printf("%d\n", p[i]);
}
}
You can use either syntax in both cases, but the particular syntax makes it more apparent what the function is operating on.
Array to pointer conversion
When an array type is used in a function parameter list, it is transformed to the corresponding pointer type:
int f(int a[2]) and int f(int* a) declare the same function.
Since the function's actual parameter type is pointer type, a function call with an array argument performs array-to-pointer conversion; ...
why does this code work?
#include <stdio.h>
void func(int v[]){
v[0] = 1;
}
int main(){
int v[5] = {0};
func(v);
for (int i = 0; i < 5; i++)
{
printf("%d ", v[i]);
}
}
The output I get from this is '1 0 0 0 0' but why? I'm not passing a pointer, why can the function change the array in my main?
Yes, you are passing a pointer.
When you write void func(int v[]) to declare your function signature, it is equivalent to writing void func(int * v).
When you write func(v) to call your function, it is equivalent to func(&v[0]).
This function declaration
void func(int v[]){
v[0] = 1;
}
is adjusted by the compiler to the declaration
void func(int *v){
v[0] = 1;
}
From the C Standard (6.7.6.3 Function declarators (including prototypes))
7 A declaration of a parameter as ‘‘array of type’’ shall be
adjusted to ‘‘qualified pointer to type’’, where the type qualifiers
(if any) are those specified within the [ and ] of the array type
derivation. If the keyword static also appears within the [ and ] of
the array type derivation, then for each call to the function, the
value of the corresponding actual argument shall provide access to the
first element of an array with at least as many elements as specified
by the size expression
On the other hand, in this call
func(v);
the array designator v is implicitly converted to a pointer to its first element.
The C Standard (6.3.2.1 Lvalues, arrays, and function designators)
3 Except when it is the operand of the sizeof operator or the unary &
operator, or is a string literal used to initialize an array, an
expression that has type ‘‘array of type’’ is converted to an
expression with type ‘‘pointer to type’’ that points to the initial
element of the array object and is not an lvalue. If the array object
has register storage class, the behavior is undefined.
That is the function call is equivalent to
func( &v[0] );
So in fact the first element of the array is passed to the function by reference through a pointer to it. Dereferencing the pointer by means of the subscript operator (the expression v[0] is equivalent to the expression *v)
v[0] = 1;
the referenced first element of the array is changed.
It is because array is internally considered as a pointer. It's an identifier for a variable of type array, which has an implicit conversion to pointer of element type.
How does char s[] act as a pointer while it looks like an array declaration?
#include<stdio.h>
void test(char s[]);
int main()
{
char s[10]="test";
char a[]=s;
test(s);
char p[]=s;
return 0;
}
void test(char s[])
{
printf(s);
}
In the context of a function parameter declaration (and only in that context), T a[N] and T a[] are the same as T *a; they declare a as a pointer to T, not an array of T.
Chapter and verse:
6.7.6.3 Function declarators (including prototypes)
...
7 A declaration of a parameter as ‘‘array of type’’ shall be adjusted to ‘‘qualified pointer to
type’’, where the type qualifiers (if any) are those specified within the [ and ] of the
array type derivation. If the keyword static also appears within the [ and ] of the
array type derivation, then for each call to the function, the value of the corresponding
actual argument shall provide access to the first element of an array with at least as many
elements as specified by the size expression.
Anywhere else, T a[]; declares a as an array with an as-yet-unspecified size. At this point the declaration is incomplete, and a cannot be used anywhere until a size has been specified, either by specifying it explicitly:
T a[N];
or using an initializer:
T a[] = { /* comma-separated list of initial values */ }
Chapter and verse, again:
6.7.6.2 Array declarators
...
3 If, in the declaration ‘‘T D1’’, D1 has one of the forms: D[ type-qualifier-listopt assignment-expressionopt ]
D[ static type-qualifier-listopt assignment-expression ]
D[ type-qualifier-list static assignment-expression ]
D[ type-qualifier-listopt * ]
and the type specified for ident in the declaration ‘‘T D’’ is ‘‘derived-declarator-type-list
T’’, then the type specified for ident is ‘‘derived-declarator-type-list array of T’’.142)
(See 6.7.6.3 for the meaning of the optional type qualifiers and the keyword static.)
4 If the size is not present, the array type is an incomplete type. If the size is * instead of
being an expression, the array type is a variable length array type of unspecified size,
which can only be used in declarations or type names with function prototype scope;143)
such arrays are nonetheless complete types. If the size is an integer constant expression
and the element type has a known constant size, the array type is not a variable length
array type; otherwise, the array type is a variable length array type. (Variable length
arrays are a conditional feature that implementations need not support; see 6.10.8.3.)
142) When several ‘‘array of’’ specifications are adjacent, a multidimensional array is declared.
143) Thus, * can be used only in function declarations that are not definitions (see 6.7.6.3).
...
6.7.9 Initialization
...
22 If an array of unknown size is initialized, its size is determined by the largest indexed
element with an explicit initializer. The array type is completed at the end of its
initializer list.
So, why are arrays as function parameters treated differently than arrays as regular objects? This is why:
6.3.2.1 Lvalues, arrays, and function designators
...
3 Except when it is the operand of the sizeof operator, the _Alignof operator, or the
unary & operator, or is a string literal used to initialize an array, an expression that has
type ‘‘array of type’’ is converted to an expression with type ‘‘pointer to type’’ that points
to the initial element of the array object and is not an lvalue. If the array object has
register storage class, the behavior is undefined.
Under most circumstances, an expression of type "N-element array of T" is converted ("decays") to an expression of type "pointer to T". If you pass an array expression as an argument to a function, like so:
int foo[10];
...
bar( foo );
what the function bar actually receives is a pointer to int, not a 10-element array of int, so the prototype for bar can be written
void bar( int *f );
"But why..." I hear you starting to ask. I'm getting to it, really.
C was derived from an earlier language called B (go figure), and in B the following things were true:
Pointer objects were declared using empty bracket notation - auto p[];. This syntax was kept for pointer function parameter declarations in C.
Array declarations set aside memory not only for the array elements, but also for an explicit pointer to the first element, which was bound to the array variable (i.e., auto v[10] would set aside 11 memory cells, 10 for the array contents, and the remaining one to store an offset to the first element).
The array subscript operation a[i] was defined as *(a + i) -- you'd offset i elements from the base address stored in the variable a and dereference the result (B was a "typeless" language, so all scalar objects were the same size).
For various reasons, Ritchie got rid of the explicit pointer to the first element of the array, but kept the definition of the subscript operation. So in C, when the compiler sees an array expression that isn't the operand of the sizeof or unary & operators, it replaces that expression with a pointer expression that evaluates to the address of the first element of the array; that way the *(a + i) operation still works the way it did in B, without actually setting aside any storage for that pointer value. However, it means arrays lose their "array-ness" in most circumstances, which will bite you in the ass if you aren't careful.
You can't assign to arrays, only copy to them or initialize them with a valid initializer (and another array is not a valid initializer).
And when you declare a function like
void test(char s[]);
it's actually the same as declaring it
void test(char *s);
What happens when you call a function taking an "array" is that the array decays to a pointer to the first element, and it's that pointer that is passed to the function.
So the call
test(s);
is the same as
test(&s[0]);
Regarding the function declaration, declaring a function taking an array of arrays is not the same as declaring a function taking a pointer to a pointer. See e.g. this old answer of mine as an explanation of why.
So if you want a function taking an array of arrays, like
void func2(char a[][X]);
it's not the same as
void func2(char **a);
Instead it's the same as
void func2(char (*a)[X]);
For more "dimensions" it doesn't change anything, e.g.
void func3(char a[][X][Y]);
is the same as
void func3(char (*a)[X][Y]);
char a[] is an array and not a pointer, the inittialization is invalid.
s is an array, too, but in the context of an expression it evaluates to a pointer to its first element.
char a[] is only valid if you have a following initializer list. It has to be an array of characters. As a special case for strings, C allows you to type an array of characters as "str", rather than {'s','t','r','\0'}. Either initializer is fine.
In the code char a[]=s;, s is an array type and not a valid initializer, so the code will not compile.
void test(char s[]) is another special case, because arrays passed as parameters always get replaced by the compiler with a pointer to the first element. Don't confuse this with array initialization, even though the syntax is similar.
I'm going through K&R and it says array name is not a variable and it cannot be used in constructions like a=pa or a++.
Isn't s an array name here?
#include<stdio.h>
main(){
printf("%d", strlen("test"));
}
int strlen(char s[])
{
int n;
for (n = 0; *s!= '\0';s++) // why is s++ valid even though it is declared as an array
n++;
return n;
}
No, in this context it's a pointer to a char. Your function declaration is completely equivalent to:
int strlen(char *s)
As you'll see, it's actually impossible to pass an array to a function: a pointer to the first element is what is actually passed.
Thus, since s is actually a pointer and not an array, you're free to modify it as you please.
From the horse's mouth:
6.3.2.1 Lvalues, arrays, and function designators
...
3 Except when it is the operand of the sizeof operator, the _Alignof operator, or the
unary & operator, or is a string literal used to initialize an array, an expression that has
type ‘‘array of type’’ is converted to an expression with type ‘‘pointer to type’’ that points
to the initial element of the array object and is not an lvalue. If the array object has
register storage class, the behavior is undefined.
The expression "test" is a string literal, which has type "5-element array of char". When you pass "test" as a parameter of strlen, by the rule above, what actually gets passed is a pointer whose value is the address of the first character in "test".
Which brings us to...
6.7.6.3 Function declarators (including prototypes)
...
7 A declaration of a parameter as ‘‘array of type’’ shall be adjusted to ‘‘qualified pointer to
type’’, where the type qualifiers (if any) are those specified within the [ and ] of the
array type derivation. If the keyword static also appears within the [ and ] of the
array type derivation, then for each call to the function, the value of the corresponding
actual argument shall provide access to the first element of an array with at least as many
elements as specified by the size expression.
So in the prototype for strlen, char s[] is equivalent to char *s; s is declared as a pointer to char, not an array of char.
C's treatment of arrays is a bit baroque compared to other languages. That's due in part to the BCPL and B heritage. If you're curious as to why, you can read dmr's The Development of the C Language for some insights.
No, acctually s is a pointer name.
The declaration int strlen(char s[]) is same as int strlen(char *s)
When Char s[]={...} is declared address is attached to s, which never changes (like constant pointer) and anything try to change this property becomes an illegal operation such as s++.
But In function call int strlen(char s[]) , array is passed as pointer.