关于指针和多维数组的困惑 [英] Confusion about pointers and multidimensional arrays

查看:27
本文介绍了关于指针和多维数组的困惑的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

如果可以:

MyFunction(int *array, int size){for(int i=0 ; i

为什么下面不是?

MyFunction(int **array, int row, int col){for(int i=0 ; i

解决方案

首先,一些标准语言:

<块引用>6.3.2.1 左值、数组和函数指示符
...
3 除非它是 sizeof 运算符或一元 & 运算符的操作数,或者是用于初始化数组的字符串文字,否则具有类型数组"类型的表达式将转换为具有类型指针"类型的表达式指向数组对象的初始元素,而不是左值.如果数组对象具有寄存器存储类,则行为未定义.

给定声明

int myarray[3][3];

myarraytypeint 的三元素数组的三元素数组".按照上面的规则,当你写

MyFunction(myarray, 3, 3);

表达式 myarray 的类型从int 的 3 元素数组的 3 元素数组隐式转换(衰减")" 到 "指向 int 的 3 元素数组的指针",或 int (*)[3].

因此,您的函数原型需要

int MyFunction(int (*array)[3], int row, int col)

注意int **arrayint (*array)[3]相同;指针算法会有所不同,因此您的下标不会指向正确的位置.请记住,数组索引是根据指针算术定义的:a[i] == *(a+i), a[i][j] == *(*(a + i) + j).a+i 将产生不同的值,具体取决于 aint ** 还是 int (*)[N].

这个特殊的例子假设你总是传递一个 int 的 Nx3 元素数组;如果你想处理任何 NxM 大小的数组,那不是非常灵活.解决此问题的一种方法是显式传递数组中第一个元素的地址,因此您只需传递一个简单的指针,然后手动计算正确的偏移量:

void MyFunction(int *arr, int row, int col){内部 i, j;for (i = 0; i < row; i++)for (j = 0; j 

因为我们传递了一个简单的指针给int,所以我们不能在MyFunc中使用双下标;arr[i] 的结果是一个整数,而不是一个指针,所以我们必须在一个下标操作中计算到数组的完整偏移量.请注意,此技巧仅适用于真正的多维数组.

现在,** 可以表示以二维结构组织的值,但以不同方式构建的值.例如:

void AnotherFunc(int **arr, int row, int col){内部 i, j;for (i = 0; i < row; i++)for (j = 0; j 

按照上面的规则,当表达式 d0d1d2 出现在 a,它们的类型都从int的三元素数组"转换为int的指针".类似地,当表达式 a 出现在对 AnotherFunc 的调用中时,其类型将从指向 int 的指针的三元素数组"转换为指向 int 的指针的指针".

请注意,在 AnotherFunc 中,我们对两个维度都下标,而不是像在 MyFunc 中那样计算偏移量.这是因为 a 是一个 指针 值的数组.表达式arr[i] 为我们获取从位置arr 偏移的第i 个指针 值;然后我们找到从该指针值偏移的第 j 个整数值.

下表可能会有所帮助 - 它显示了各种数组表达式的类型以及它们根据声明衰减到的内容(T (*)[N] 是指针类型,而不是数组类型,所以它不会衰减):

<前>声明表达式类型隐式转换(衰减)为----------- ---------- ---- --------------------------------T a[N] a T [N] T *&a T (*)[N]*在[i] TT a[M][N] a T [M][N] T (*)[N]&a T (*)[M][N]*a T [N] T *[i] T [N] T *&a[i] T (*)[N]*a[i] T[i][j] TT 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] Ta[i][j][k] T

高维数组的模式应该很清楚.

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);
}

解决方案

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.

这篇关于关于指针和多维数组的困惑的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆