First, some standard language:
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.
Given the declaration
int myarray[3][3];
the type of myarray
is "3-element array of 3-element array of int
". Going by the rule above, when you write
MyFunction(myarray, 3, 3);
the expression myarray
has its type implicitly converted ("decay") from "3-element array of 3-element array of int
" to "pointer to 3-element array of int
", or int (*)[3]
.
Thus, your function prototype would need to be
int MyFunction(int (*array)[3], int row, int col)
Note that int **array
is not the same as int (*array)[3]
; the pointer arithmetic will be different, so your subscripts won't wind up pointing to the right places. Remember that array indexing is defined in terms of pointer arithmetic: a[i]
== *(a+i)
, a[i][j] == *(*(a + i) + j)
. a+i
will yield a different value depending on whether a
is an int **
or an int (*)[N]
.
This particular example assumes you're always passing an Nx3-element array of int
; not terribly flexible if you want to deal with any NxM-sized array. One way to get around this would be to explicitly pass the address of the first element in the array, so you're just passing a simple pointer, and then compute the proper offset manually:
void MyFunction(int *arr, int row, int col)
{
int i, j;
for (i = 0; i < row; i++)
for (j = 0; j < col; j++)
printf("%d", a[i*col+j]);
}
int main(void)
{
int myarray[3][3] = {{1,2,3},{4,5,6},{7,8,9}};
...
MyFunction(&myarray[0][0], 3, 3);
Since we pass a simple pointer to int
, we can't use a double subscript in MyFunc
; the result of arr[i]
is an integer, not a pointer, so we have to compute the full offset into the array in the one subscript operation. Note that this trick will only work for truly multidimensional arrays.
Now, a **
can indicate values that are organized in a 2-D structure, but one that was built a different way. For example:
void AnotherFunc(int **arr, int row, int col)
{
int i, j;
for (i = 0; i < row; i++)
for (j = 0; j < col; j++)
printf("%d", arr[i][j]);
}
int main(void)
{
int d0[3] = {1, 2, 3};
int d1[3] = {4, 5, 6};
int d2[3] = {7, 8, 9};
int *a[3] = {d0, d1, d2};
AnotherFunc(a, 3, 3);
...
}
Going by the rule above, when the expressions d0
, d1
, and d2
appear in the initializer for a
, their types are all converted from "3-element array of int
" to "pointer to int
". Similarly, when the expression a
appears in the call to AnotherFunc
, its type is converted from "3-element array of pointer to int
" to "pointer to pointer to int
".
Note that in AnotherFunc
we subscript both dimensions instead of computing the offset like we did in MyFunc
. That's because a
is an array of pointer values. The expression arr[i]
gets us the i'th pointer value offset from the location arr
; we then find the j'th integer value offset from that pointer value.
The following table might help - it shows the types of various array expressions and what they decay to based on their declarations (T (*)[N]
is a pointer type, not an array type, so it doesn't decay):
Declaration Expression Type Implicitly Converted (Decays) to
----------- ---------- ---- --------------------------------
T a[N] a T [N] T *
&a T (*)[N]
*a T
a[i] T
T a[M][N] a T [M][N] T (*)[N]
&a T (*)[M][N]
*a T [N] T *
a[i] T [N] T *
&a[i] T (*)[N]
*a[i] T
a[i][j] T
T a[L][M][N] a T [L][M][N] T (*)[M][N]
&a T (*)[L][M][N]
*a T [M][N] T (*)[N]
a[i] T [M][N] T (*)[N]
&a[i] T (*)[M][N]
*a[i] T [N] T *
a[i][j] T [N] T *
&a[i][j] T (*)[N]
*a[i][j] T
a[i][j][k] T
The pattern for higher-dimensional arrays should be clear.