Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
333 views
in Technique[技术] by (71.8m points)

c - 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 following 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);
}
See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

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.


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...