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

时间:2010-10-12 02:17:58

标签: 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);
}

为什么以下不是?

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

7 个答案:

答案 0 :(得分:13)

首先,一些standard语言:

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

鉴于声明

int myarray[3][3];

myarray类型是“int的3元素数组的3元素数组”。按照上面的规则,当你写

MyFunction(myarray, 3, 3);
表达式 myarray将其类型隐式转换(“衰减”)从“{3}元素的3元素数组”int“转换为”指向3的指针-lelement数组int“或int (*)[3]

因此,您的函数原型需要

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

请注意,int **array int (*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)
{
  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);

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

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

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

按照上述规则,当d0d1d2表达式出现在a的初始值设定项中时,它们的类型都将从“3-”转换为元素数组int“到”指向int的指针。类似地,当表达式a出现在对AnotherFunc的调用中时,其类型将从“指向int的指针的3元素数组”转换为“指向int的指针” }”。

请注意,在AnotherFunc中,我们下标两个维度,而不是像MyFunc中那样计算偏移量。那是因为a是一个指针值的数组。表达式arr[i]使我们从位置arr获得第i个指针值;然后我们找到该指针值的第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

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

答案 1 :(得分:5)

编辑:根据您的新示例代码,我尝试根据要求提供更具针对性的答案:

无论数组的大小如何,你传递的是“指向数组的指针” - 它只是一个单个指针,尽管指针的类型可能会有所不同。

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

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

我希望你现在看到差异。当您传递int [][3]时,它实际上是指向int ** s和 NOT 数组的指针,指向2D数组。

实际int *的示例如下:

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 }; 是一个包含3个array的数组,并将其作为参数传递将产生int *


原始回答:

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

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

char (*)[COLS]

答案 2 :(得分:1)

其他人已经总结了很多。 int ** A表示A是指向数组的指针,而不是对2-D数组的引用。 但是,这并不意味着它不可用。由于C中的数据以行主顺序存储,一旦知道行长度,检索数据应该很容易

答案 3 :(得分:0)

因为指针指针与指向数组的指针的类型不同。有关详细信息,请参阅pointers to pointers and pointer arrays

此外,这有一些很好的信息:http://c-faq.com/aryptr/index.html

答案 4 :(得分:0)

第一个例子是可能的,因为当作为函数参数传递时,数组会退化为指针。

第二个示例不起作用,因为int[3][3]退化为int (*)[3]不是双指针int **。情况就是这样,因为2D数组在内存中是连续的,没有这些信息,编译器就不会知道如何访问第一行之后的元素。考虑一个简单的数字网格:

1  2  6 
0  7  9

如果我们将这些数字存储在数组int nums[6]中,我们如何索引数组以访问元素7?当然,1 * 3 + 1或更一般地row * num-columns + columnnums[2][3]。要访问第一行之后的任何元素,您需要知道网格有多少列。

当您将数字存储为row * num-columns + column时,编译器使用与使用1D数组手动完全相同的{{1}}算法,它只是对程序员隐藏。因此,在传递2D数组时必须传递列数,以便编译器能够执行此算法。

在许多其他语言中,数组携带有关其大小的信息,无需在将多维数组传递给函数时手动指定维度。

答案 5 :(得分:0)

如果你想要得到更多的答案,也许我们可以期待一个更“重要”的问题。你的想法有两个问题:

  1. 使用时的二维数组int A[3][3] 在一个表达衰败到 因此,它的第一个元素的地址 类型为int (*)[3]的指针。为了能够通过数组,您必须使用&A[0][0]来获取指向第一个“内部”成员的指针。
  2. 在你的功能里面操作 从那时起无法执行A[i][j] 你的编译器没有任何信息 行长,那里。

答案 6 :(得分:0)

此代码存在两个主要问题。

MyFunction(int **array, int row, int col);

首先,int **array是错误的类型。这是指向指针的指针,而

int array[3][3] = {0, 1, 2, 3, 4, 5, 6, 7, 8};

是一个多维数组。构成这个多维数组的内存都是一个块,并且从该数组开头到该数组的任何元素的偏移量是根据对该数组中大小的了解计算的。 / p>

int *A[99];

这是一个指向整数的指针数组。指向的整数可能是内存中几个整数中的第一个,这意味着它们实际上指向整数数组。

在许多情况下,当您在程序中使用数组的名称时,它会计算指向数组开头的指针。如果你说:

int array[3][3] = {0, 1, 2, 3, 4, 5, 6, 7, 8};
printf("%p %p %p\n", array, array[0], &(array[0][0]) );

你应该打印3次相同的地址,因为它们都是指同一个地址,但它们的类型不一样。最后两个的数据类型是相似的,并且可以兼容多种用途,因为array[0]将被视为指向第一行array的第一个元素的指针,并且该行本身就是一个数组

如果你说:

int **A;

你说有一个指向int指针的指针。虽然A[2][4]是一个有效的表达式,但这不是一个多维数组,其方式与:

int B[3][3];

如果您说A[1]此评估结果与int *类似B[1],那么您可以说A[1] = (int *)0x4444;,但如果您说B[1] = (int *)0x4444;会得到一个编译器错误,因为B[1]实际上是一个计算值,而不是一个变量。对于B,没有int *个变量数组 - 只是根据行大小和数组第一个成员的地址进行的一些计算。

此代码应该执行类似于您想要的操作(为了可读性,某些输出格式更改)。请注意print语句中的索引值是如何更改的。

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