关于指针和多维数组的困惑

use*_*312 5 c pointers multidimensional-array

如果可能的话:

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);
}
Run Code Online (Sandbox Code Playgroud)

为什么以下不是?

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);
}
Run Code Online (Sandbox Code Playgroud)

Joh*_*ode 13

首先,一些标准语言:

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

鉴于声明

int myarray[3][3];
Run Code Online (Sandbox Code Playgroud)

类型myarray是"的3元素数组的3元素数组int".按照上面的规则,当你写作时

MyFunction(myarray, 3, 3);
Run Code Online (Sandbox Code Playgroud)

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

因此,您的函数原型需要

int MyFunction(int (*array)[3], int row, int col)
Run Code Online (Sandbox Code Playgroud)

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

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

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);
Run Code Online (Sandbox Code Playgroud)

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

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

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);
  ...
}
Run Code Online (Sandbox Code Playgroud)

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

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

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

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

高维数组的模式应该清晰.


cas*_*nca 5

编辑:这是我尝试根据您的新示例代码提出的更为重要的答案:

不论阵列的尺寸,你通过什么是"指向数组的指针" -它只是一个单一的指针,但指针的类型可以改变.

在您的第一个示例中,int array[6]是一个包含6个int元素的数组.传递array将指针传递给第一个元素,这是一个int,因此参数类型是int *,可以等效地写为int [].

在第二个示例中,int array[3][3]是一个包含3行(元素)的3行(元素)数组int.传递array将指针传递给第一个元素,int元素是一个3 的数组.因此,类型是int (*)[3]- 指向3个元素的数组的指针,可以等效地写为int [][3].

我希望你现在看到差异.传递时int **,它实际上是指向int *s 数组的指针,而不是指向2D数组的指针.

实际的一个例子int **是这样的:

int a[3] = { 1, 2, 3 };
int b[3] = { 4, 5, 6 };
int c[3] = { 7, 8, 9 };
int *array[3] = { a, b, c };
Run Code Online (Sandbox Code Playgroud)

array是一个3 int *s 的数组,并将此作为参数传递将导致int **.


原始答案:

您的第一个示例实际上不是2D数组,尽管它以类似的方式使用.在那里,你创建了ROWS许多char *指针,每个指针都指向不同的COLS字符数组.这里有两个间接层次.

第二个和第三个示例实际上是2D数组,其中整个ROWS * COLS字符的内存是连续的.这里只有一个间接层.指向2D数组的指针不是char **,但是char (*)[COLS],您可以这样做:

char (*p)[SIZE] = arr;
// use p like arr, eg. p[1][2]
Run Code Online (Sandbox Code Playgroud)