As we know, when passing arrays as function arguments, only the first dimension's size can be empty, the others must be specified.
void my_function(int arr[5][10][15]); // OKAY!
void my_function(int arr[][10][15]); // OKAY!
void my_function(int arr[][][15]); // WRONG!!!
void my_function(int arr[][][]); // WRONG!!!
What is logic behind this? Could someone explain the main reason?
Passing arrays to a function is an illusion: arrays instantly decay to a pointer when you use them. That is, with this example:
int foo[10];
foo[1];
the way this is interpreted by the compiler, in foo[1], foo is first transformed to a pointer to element 0 of foo, and then subscripting is the same as *(pointer_to_foo + 1).
However, this is only possible with the first dimension of an array. Suppose this:
int foo[5][5];
This puts 25 integer elements contiguously in memory. It is not the same as int** foo, and not convertible to int** foo, because int** foo represents some number of pointers to some number of pointers, and they don't have to be contiguous in memory.
While it's legal to use an array type as a function parameter, the compiler builds it identically to if you had specified a pointer to the array's element type:
int foo(int bar[10]); // identical to `int foo(int* bar)`
But what happens if you pass a multi-dimensional array?
int foo(int bar[5][5]); // identical to what?
Only the first dimension of the array can undergo decay. The equivalent signature for int foo here would be:
int foo(int (*bar)[5]); // identical to `int foo(int bar[5][5])`
where int (*bar)[5] is a pointer to an array of 5 integers. Stacking up more dimensions doesn't change the idea, only the first dimension will decay, and the other dimensions need to have a known size.
In other words, you can skip the first dimension because the compiler doesn't care about its size, as it instantly decays. However, subsequent dimensions do not decay at the call site, so you need to know their size.
Arrays in C and C++ are stored contiguously in memory. In the case of a multidimensional array, this means you have an array of arrays.
When you traverse an array, you don't necessarily need to know how many elements are in the array. You do however need to know the size of each element of the array so you can jump to the correct element.
That's why int arr[][][15] is incorrect. It says that arr is an array of unknown size elements are of type int [][15]. But this means you don't know how big each array element is, so you don't know where each array element it in memory.
Starting with two bits of standardese:
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.
...
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.
C 2011 online draft
Lovely. What does all that mean?
Let's start with the declaration of an array of some arbitrary type T:
T arr[N];
The type of the expression arr is "N-element array of T" (T [N]). Unless that expression is the operand of the unary &, sizeof, or _Alignof operators as specified in 6.3.2.1/3 above, then the type of the expression is converted ("decays") to "pointer to T" (T *) and the value of the expression is the address of the first element - i.e., &arr[0].
When we pass arr as a parameter to a function, as in
foo( arr );
what foo actually receives is a pointer, not an array, and you would write the function prototype as
void foo( T *arr )
Or...
As a notational convenience, C allows you to declare the formal parameter using array notation:
void foo( T arr[N] )
or
void foo( T arr[] )
In this case, both T arr[N] and T arr[] are "adjusted" to T *arr as per 6.7.6.3/7 above, and all three forms declare arr as a pointer (this is only true for function parameter declarations).
That's easy enough to see for 1D arrays. But what about multidimensional arrays?
Let's replace T with an array type A [M]. Our declaration now becomes
A arr[N][M]; // we're creating N M-element arrays.
The type of the expression arr is "N-element array of M-element arrays of A" (A [M][N]). By the rule above, this "decays" to type pointer to *M-element array* ofA" (A (*)[M]`). So when we call
foo( arr );
the corresponding prototype isvoid foo( A (*arr)[M] )1
which can also be written as
void foo( A arr[N][M] )
or
void foo( A arr[][M] );
Since T arr[N] and T arr[] are adjusted to T *arr, then A arr[N][M] and A arr[][M] are adjusted to A (*arr)[M].
Let's replace A with another array type, R [O]:
R arr[N][M][O];
The type of the expression arr decays to R (*)[M][O], so the prototype of foo can be written as
void foo( R (*arr)[M][O] )
or
void foo( R arr[N][M][O] )
or
void foo( R arr[][M][O] )
Are you starting to see the pattern yet?
When a multi-dimensional array expression "decays" to a pointer expression, only the first (leftmost) dimension is "lost", so in a function prototype declaration, only the leftmost dimension my be left blank.
Since the subscript [] operator has a higher precedence than the unary * operator, A *arr[M] would be interpreted as "M-element array of pointers to A", which is not what we want. We have to explicitly group the * operator with the identifier to properly declare it as a pointer to an array.
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)
{
...
}
I found this code in internet.And i am confused from 1 hour.
What i found weird is that array b[] is inside met2() function with local scope. And there is another array a[] which is inside met1() function with it's local scope . But How could value of a[] is transferred to b[] array.
And Most importantly, both function are not returning any values.
This is so confusing. Please Help me out here. I searched online but nobody had asked such questions.
#include <stdio.h>
int main()
{
met2();
return 0;
}
void met1(int a[1])
{
a[0]=199;
}
void met2()
{
int b[1];
met1(b);
printf("%d",b[0]);
}
Except when it is the operand of the sizeof or unary & operators, or is a string literal used to initialize a character array in a declaration, an expression of type "N-element array of T" is converted ("decays") to an expression of type "pointer to T" and the value of the expression is the address of the first element of the array.
When you call
met1(b);
the expression b is converted from type "1-element array of int" to type “pointer to int", and the value of the expression is the address of b[0].
In the function prototype
void met1(int a[1])
the parameter declaration int a[1] is "adjusted" to int *a - it’s actually being declared as a pointer (which is handy, since that’s what the function actually receives).
So whenmet2 calls met1, it’s passing the address of the first element of b. The [] operator can be used on pointers as well as arrays (it’s actually defined in terms of pointer arithmetic - a[i] is interpreted as *(a + i)).
So writing a[0] = 199; in met1 is equivalent to writing b[0] = 199; in met2.
The key here is to understand that whenever we declare an array as parameter of a function, it gets adjusted to a pointer to its first element. C17 6.7.6.3 emphasis mine:
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.
This means that void met1(int a[1]) and void met1(int* a) are 100% equivalent. The former gets silently translated to the latter by the compiler.
This is quite confusing because int a[1] looks like an array declaration and one might easily get idea the idea that the array is passed by value. But passing arrays by value isn't possible in C.
In your case, the local array b gets passed to met1 as a pointer to its first element. With a[0] = ... that pointed-at element is changed.
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.
Why when using a matrix as a parameter of a function we must specify the number of columns but we don't have to indicate number of rows?
I assume you're talking about a function definition like
void foo( int matrix[][COLS] ) { ... }
This requires a little background...
Except when it is the operand of the sizeof or unary & operators, or is a string literal being used to initialize another array in a declaration, an expression of type "N-element array of T" will be converted ("decay") to an expression of type "pointer to T", and the value of the expression will be the address of the first element of the array. So, given the declaration
int arr[10];
the expression arr will have type "10-element array of int"; unless this expression is the operand of the sizeof or unary & operators, it will be converted ("decay") to an expression of type "pointer to int". So if we pass that array to a function, like
blurga( arr );
what blurga receives is a pointer to int, not an array of int. So we can define blurga as
void blurga( int *ap )
{
// do something with ap[i]
}
The formal parameter ap has type "pointer to int". Note that you can use the [] operator on pointer expressions as well as array expressions1.
Now, in the context of a formal parameter declaration, T a[N] and T a[] are interpreted as T *a; all three declare a as a pointer to T, not an array of T. So we can rewrite that function definition as
void blurga( int ap[] )
{
// do something with ap[i]
}
Again, int ap[] is interpreted exactly the same as int *ap.
Now let's look at a two-dimensional array:
int blah[10][10];
The expression blah has type "10-element array of 10-element array of int". Going by the conversion rule above, the expression blah will decay to an expression of type "pointer to 10-element array of int", or int (*)[10]. So if we pass blah to a function, what the function receives is a pointer to an array, not an array of arrays:
void bletch( int (*barr)[10] );
As before T a[] is interpreted as T *a, so we can write that declaration as
void blech( int barr[][10] ); // barr[] == (*bar)
Just remember that barr is a pointer type, not an array type.
1. In fact, a[i] is defined as the result of *(a + i); given a pointer value a, we offset i elements from that address and dereference the result. So basically, the conversion rule still applies; the array expression a is being converted to a pointer expression, and that pointer expression is what the [] is being applied to.
I read these two different types of declaration of multidimensional array in formal parameters of a function in C.
int c[][10];
int (*c)[10];
How these two are same ? I am not getting the feel of it. Can anyone explain this with some example what the second one is trying to do?
Sorry if this has been previously asked..Please redirect me to the duplicate if any.
As a function argument, int* c and int c[] or even int c[7] are identical. See C FAQ.
The [10] part only tells the compiler how to do the arithmetic for accessing an element in the array - e.g. c[3][5]. Both these declarations are for a multidimensional array whose second dimension (as far as the compiler is concerned, inside this function) is of size 10.
Example:
#include <stdio.h>
int sum_all(int c[][2], int len) {
int res = 0, i ,j;
for (i=0; i < len; i++)
for (j=0; j < 2; j++)
res += c[i][j];
return res;
}
int main() {
int a[3][2] = { { 1, 2}, {3, 4}, {5, 6} };
printf("sum is %d\n", sum_all(a, 3));
return 0;
}
Note that the array is not checked to be of size 2, in this example. We could have passed a single dimensional array. The compiler does not care; we only told him how to access the elements in this array.
N1570:
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, within the context of a function parameter declaration, T a[N] and T a[] are both equivalent to T *a; all three declare a as a pointer to T. In this particular case, T is "10-element array of int".
This goes hand-in-hand with the following:
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.
Suppose you have an array declared as
int arr[5][10];
When you pass the array expression arr to a function, such as
foo( arr );
the array expression arr is converted from type "5-element array of 10-element array of int" to type "pointer to 10-element array of int", and that pointer value is what gets passed to the function. So your function prototype for foo would read as
void foo( int (*c)[10] )
or
void foo( int c[][10] )
or even
void foo( int c[5][10] )
but in all three cases, c is a pointer to an array, not a 2D array.
Consider what happens when you define int c[5][10] and then pass c to a routine. The array c will automatically be converted to a pointer to its first element, which is an array of 10 int.
So the language designers arranged it so that when you declare a parameter with c[][10], it is automatically adjusted to match the conversion that will happen. The compiler changes a parameter of type array of array of int to a parameter of type pointer to array of int.