I just came across this function call/declaration pair in C, and I'm a bit confused (I'm a newbie here, but I haven't seen this addressed in any of the material I've been using to learn). The function declaration is given as bool read_matrix(double a[][M], int n, int m), but it is called by read_matrix((double (*)[M]) a, n, m). (M is an integer set by #define.) How do these parameters line up with one another? For that matter, what type of object is (*)[M]?
Thanks for clearing up the confusion.
When a function parameter declared with an array type like in this function declaration
bool read_matrix(double a[][M], int n, int m);
then it is adjusted by the compiler to pointer to the element type.
So this function declaration is equivalent to the declaration
bool read_matrix(double ( *a )[M], int n, int m);
On the other hand array designators used in expressions as for example as an argument are converted to pointers to their first elements.
So if in a caller of the function you have an array declared like
double a[N][M];
then passed to the function like
read_matrix( a, N, M );
it is converted to pointer to its first element of the type int ( ^ )[M].
As for the casting in the call you showed
read_matrix((double (*)[M]) a, n, m)
then if a is an array declared as shown above then the casting is redundant. This conversion takes place implicitly.
Except when it is the operand of the sizeof, _Alignof, 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" 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.
When you pass an array expression as a function argument, what the function actually receives is a pointer value:
void foo( T *a ) // equivalent to T a[] and T a[N]
{
...
}
int main( void )
{
T arr[N];
...
foo( arr );
...
}
As a "convenience" C allows you to use array notation for a function parameter declaration, but be aware it will be "adjusted" to be a pointer declaration because the parameter is a pointer value, not an array - T a[N] and T a[] will be interpreted as T *a. Note that this is only true in a function parameter declaration, not a regular variable declaration.
Now, just for giggles, replace T with the array type double [M]. So instead of being an N-element array of T, arr is now an N-element array of M-element arrays of double:
int main( void )
{
double arr[N][M];
...
foo( arr );
...
}
In the call to foo, the expression arr "decays" from type "N-element array of M-element arrays of double" to "pointer to M-element array of double", or double (*)[M]. So our declaration of foo looks like this:
void foo( double (*a)[M] ) // equivalent to double a[][M] and double a[N][M]
{
...
}
And this is why the two seemingly different declarations are equivalent - in the context of a function parameter declaration, double a[][M] is interpreted as double (*a)[M]. Again, this is only true for function parameter declarations, not regular variable declarations.
Related
I'm having the following below.
When I give values to the array through the read_data value, then values of the array can be read by main() without read_data returning anything. Does the
grade_table2 array points to the same address in memory like grade_table?
#include <stdio.h>
#define NUM_STUDENTS 3
#define NUM_COURSES 2
void read_data(float grade_table2[][NUM_COURSES])
{
int i,j;
for(i=0;i<NUM_STUDENTS;++i)
for(j=0;j<NUM_COURSES;++j)
scanf("%f",&grade_table2[i][j]);
}
int main()
{
float grade_table[NUM_STUDENTS][NUM_COURSES];
float average_per_student[NUM_STUDENTS];
float average_per_course[NUM_COURSES];
int i,j;
read_data(grade_table);
for(i=0;i<NUM_STUDENTS;++i){
for(j=0;j<NUM_COURSES;++j)
printf("%.2f ",grade_table[i][j]);
printf("\n");
}
}
Unless 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 "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.
In plain English, when you passed grade_table to read_data, you actually passed a pointer to the first element, so any updates to grade_table2 in read_data are reflected in grade_table in main.
In the context of a function parameter declaration, T a[N] and T a[] are interpreted as T *a. Note that this is only true for function parameters.
This "decay" rule only applies to array expressions. struct and union arguments are passed by value like any scalar type (int, float, etc.).
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.
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.
I have this function that takes a pointer of an array (in order to modify it from within the function)
int func_test(char *arr[]){
return 0;
}
int main(){
char var[3];
func_test(&var);
return 0;
}
When I try to compile this I get :
passing argument 1 of ‘func_test’ from incompatible pointer type
Why is this problem, and how I pass a pointer to that array in this case?
char * arr[] is not a pointer to an array; it is an array of pointers. Declarations in C are read first from the identifier towards the right, then from the identifier towards the left. So:
char * arr[];
// ^ arr is...
// ^ an array of...
// ^ pointers to...
// ^ char
A pointer to an array is a type (*varname)[], in your case, a char (*arr)[].
You are passing the address of a pointer. I think you want this:
int func_test(char arr[]){
arr[0] = 'a';//etc.
return 0;
}
int main(){
char var[3];
func_test(var);
return 0;
}
char var[3] is an array that holds 3 characters, not 3 pointers to characters - char *arr[] denotes an array that holds pointers to characters.
So you can go like this:
char *var[3];
func_test(var);
Note that the ampersand is not needed because array identifiers automatically decay to pointers of the corresponding type, in this case char **.
That's because the name of an array is already a pointer to it, so use func_test(var).
&var will have type char**
This is an array of pointers
char *arr[]
This is an address of a pointer
&var
So you are passing something different to what the function expects
C's treatment of arrays is a little confusing at first.
Except when it's an operand of either the sizeof or unary & operators, or when it's a string literal being used to initialize another array in a declaration, an expression with type "N-element array of T" will be implicitly converted to type "pointer to T", and its value will be the address of the first element in the array.
Assume the following declaration:
int x[10];
The type of the expression x is "10-element array of int". However, when that expression appears as, say, a parameter to a function:
foo(x);
the type of x is implicitly converted ("decays") to type "pointer to int". Thus, the declaration of foo needs to be
void foo(int *p);
foo receives an int *, not an int [10].
Note that this conversion also occurs for something like
i = x[0];
Again, since x isn't an operand of sizeof or &, its type is converted from "10-element array of int" to "pointer to int". This works because array subscripting is defined in terms of pointer arithmetic; the expression a[n] is equivalent to *(a+n).
So for your code, you'd write
int func_test(char *arr) { return (0); }
int main(void)
{
char var[3];
func_test(var); // no & operator
return 0;
}
Postfix operators like [] have higher precedence than unary operators like *, so a declaration like T *a[N] is interpreted as T *(a[N]), which declares an array of pointer to T. To declare a pointer to an array, you have to use parentheses to explicitly group the * operator with the array name, like T (*a)[N].
Here's a handy table of array declarations, expressions, and types:
Declaration: T a[N]; // a is an N-element array of T
Expression Type Decays To
---------- ---- ---------
a T [N] T *
&a T (*)[N]
a[i] T
&a[i] T *
Declaration: T *a[N]; // a is an N-element array of pointer to T
Expression Type Decays To
---------- ---- ---------
a T *[N] T **
&a T *(*)[N]
a[i] T *
*a[i] T
&a[i] T *
Declaration: T (*a)[N] // a is a pointer to an N-element array of T
Expression Type Decays To
---------- ---- ---------
a T (*)[N]
&a T (**)[N]
*a T [N] T *
(*a)[i] T