Confusion about pointers and multidimensional arrays
If the following is possible:
MyFunction(int *array, int size)
{
for(int i=0 ; i<size ; i++)
{
printf(“%d”, array[i]);
}
}
main()
{
int array[6] = {0, 1, 2, 3, 4, 5};
MyFunction(array, 6);
}
Why the foll开发者_Python百科owing is not?
MyFunction(int **array, int row, int col)
{
for(int i=0 ; i<row ; i++)
{
for(int j=0 ; j<col ; j++)
{
printf(“%d”, array[i][j]);
}
}
}
main()
{
int array[3][3] = {0, 1, 2, 3, 4, 5, 6, 7, 8};
MyFunction(array, 3, 3);
}
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.
Edit: Here's my attempt at a more to-the-point answer as requested and based on your new example code:
Regardless of the array dimensions, what you pass is a "pointer to an array" - it's only a single pointer, though the type of the pointer can vary.
In your first example, int array[6]
is an array of 6 int
elements. Passing array
passes a pointer to the first element, which is an int
, hence the parameter type is int *
, which can be equivalently written as int []
.
In your second example, int array[3][3]
is an array of 3 rows (elements), each containing 3 int
s. Passing array
passes a pointer to the first element, which is an array of 3 int
s. Hence the type is int (*)[3]
- a pointer to an array of 3 elements, which can be equivalently written as int [][3]
.
I hope you see the difference now. When you pass an int **
, it is actually a pointer to an array of int *
s and NOT a pointer to a 2D array.
An example for an actual int **
would be something like this:
int a[3] = { 1, 2, 3 };
int b[3] = { 4, 5, 6 };
int c[3] = { 7, 8, 9 };
int *array[3] = { a, b, c };
Here array
is an array of 3 int *
s, and passing this as an argument would result in an int **
.
Original answer:
Your first example isn't really a 2D array, although it is used in a similar way. There, you're creating ROWS
number of char *
pointers, each of which points to a different array of COLS
characters. There are two levels of indirection here.
The second and third examples are actually 2D arrays, where the memory for the entire ROWS * COLS
characters is contiguous. There is only one level of indirection here. A pointer to a 2D array is not char **
, but char (*)[COLS]
, so you can do:
char (*p)[SIZE] = arr;
// use p like arr, eg. p[1][2]
The others have pretty much summed it up. int **A means that A is a pointer to an array and not a reference to a 2-D array. However, that does not mean it is not usable. Since the data in C is stored in row-major order, once you know the row length, retrieving the data should be easy
Because a pointer pointer isn't the same type as a pointer to an array. See pointers to pointers and pointer arrays for details.
Also, this has some good info: http://c-faq.com/aryptr/index.html
The first example is possible because arrays degenerate to pointers when passed as function parameters.
The second example does not work because int[3][3]
degenerates to int (*)[3]
, not a double pointer int **
. This is the case because 2D arrays are contiguous in memory, and without this information the compiler would not know how to access elements past the first row. Consider a simple grid of numbers:
1 2 6
0 7 9
If we were storing these numbers in an array int nums[6]
, how would we index into the array to access the element 7? By 1 * 3 + 1
, of course, or more generally, row * num-columns + column
. In order to access any element past the first row, you need to know how many columns the grid has.
When you store the numbers as nums[2][3]
, the compiler uses the exact same row * num-columns + column
arithmetic as you do manually with a 1D array, it's just hidden from the programmer. Therefore, you must pass the number of columns when passing a 2D array in order for the compiler to be able to carry out this arithmetic.
In many other languages, arrays carry information about their size, eliminating the need to manually specify dimensions when passing multi-dimensional arrays to functions.
Perhaps we can expect a more "to the point" question, if you want to have a more to the point answer. Your idea has two problems:
- a 2D array
int A[3][3]
when used in an expression decays to the address of its first element thus to a pointer of typeint (*)[3]
. To be able to pass the array through you'd have to use&A[0][0]
to get a pointer to the first "inner" member. - inside your function the operation
A[i][j]
can't be performed since your compiler has no information of the row length, there.
There are two main issues with this code.
MyFunction(int **array, int row, int col);
The first is that int **array
is the wrong type to use. This is a pointer to a pointer, while
int array[3][3] = {0, 1, 2, 3, 4, 5, 6, 7, 8};
is a multidimensional array. The memory that makes up this multidimensional array is all one chunk, and the offset from the beginning of this to any element of this array is calculated based on knowledge of the size of a row in this array.
int *A[99];
This is an array of pointers to integers. The integers that are pointed to could be the first of several integers in memory, which means that these actually point to arrays of integers.
Under many circumstances when you use the name of an array in a program it evaluates to a pointer to the beginning of the array. If you say:
int array[3][3] = {0, 1, 2, 3, 4, 5, 6, 7, 8};
printf("%p %p %p\n", array, array[0], &(array[0][0]) );
You should get the same address printed 3 times because they all refer to the same address, but their types are not the same. The data type of the last two are similar and compatible for many purposes, since array[0]
would be treated as a pointer to the first element of the first row of array
and this row is an array all by itself.
If you say:
int **A;
You are saying that there is a pointer to a pointer to an int
. While A[2][4]
is a valid expression, this is not a multidimensional array in the same way as:
int B[3][3];
If you say A[1]
this evaluates to an int *
similar to B[1]
would, except that you can say A[1] = (int *)0x4444;
, but if you said B[1] = (int *)0x4444;
you would get a compiler error because B[1]
is actually a calculated value, not a variable. With B
there is no array of int *
variables -- just some calculations based on the row size and the address of the very first member of the array.
This code should do something similar to what you wanted (some output formatting changes for readability). Note how the index value in the print statement is changed.
MyFunction(int *array, int row, int col)
{
int x = 0;
for(int i=0 ; i<row ; i++ )
{
for(int j=0 ; j<col ; j++)
{
printf(“%d ”, array[x++]);
}
printf("\n");
}
}
main()
{
int array[3][3] = {0, 1, 2, 3, 4, 5, 6, 7, 8};
MyFunction(array, 3, 3);
}
精彩评论