【问题标题】:Why can't we use double pointer to represent two dimensional arrays?为什么我们不能用双指针来表示二维数组?
【发布时间】:2011-05-27 03:17:33
【问题描述】:

为什么我们不能用双指针来表示二维数组?

arr[2][5] = {"hello","hai"};
**ptr = arr;

为什么双指针(**ptr)在这个例子中不起作用?

【问题讨论】:

  • 您编译的哪些代码与您问题中的代码相似?
  • 远离多维数组(可能是小的数组除外)。
  • @Alexandre C. 多维数组简单、有用且高效。你为什么要逃离他们?
  • @Shahbaz:它们并不简单,如果你没有 C99,它们也没有用,因为它们不能有可变长度。
  • 我把问题倒过来了,以免误导以后的读者。

标签: c


【解决方案1】:

语言与表示之间永远存在脱钩。

在某种程度上,C 几乎是Dissendium

以上面的例子为例,我们试图将二维数组赋值或转换成另一种形式,

#include <stdio.h> 
  
int main() 
{ 
        char array[2][6] = {"hello", "hai"};
        int h, i;
        for (h = 0; h < 2; h++) 
                for (i = 0; i < 6; i++) 
                        printf("%c\n", *(*(array+h) + i));
        char (*p)[6] = array;
        for (h = 0; h < 2; h++) 
                for (i = 0; i < 6; i++) 
                        printf("%c\n", *(*(p+h) + i));
        char **x = array;
        for (h = 0; h < 2; h++) 
                        printf("%c\n", *(x+h));
} 

还有输出,

h
e
l
l
o

h
a
i



h
e
l
l
o

h
a
i



h
i

如今的编译器非常聪明,它们可能只会发出警告而不是失败,即

main.c:14:13: warning: initialization from incompatible pointer type [enabled by default]
  char **x = array;

至此,我们应该可以理解双指针不是双数组了。

第一个h 来自hello,第二个i 来自hai。注意距离是 8 个字节,等于 指针的大小

另外,我们也可以,

char *y[2];
for (h = 0; h < 2; h++) 
        y[h] = array[h];
for (h = 0; h < 2; h++)
        printf("%s\n", y[h]);
for (h = 0; h < 2; h++)
        for (i = 0; i < 6; i++)
                printf("%c\n", *(*(p+h) + i));

但是这个赋值不是一行语句。

还有输出,

hello
hai
h
e
l
l
o

h
a
i

【讨论】:

    【解决方案2】:

    我会试着画出方法

    int array[10][6];
    

    int **array2 = malloc(10 * sizeof *array2);
    for (int i = 0; i < 10; ++i)
        array2[i] = malloc(6 * sizeof **array2);
    

    在记忆中的样子以及它们有何不同(而且它们不能相互转换)

    array 看起来像:

     _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
    | | | | | | | | | | | | | ..............| | | (10*6 elements of type int)
     - - - - - - - - - - - - - - - - - - - - - -
    < first row >< second row> ...
    

    array2 看起来像:

     _ _ _ _ _ _ _ _ _ _ 
    | | | | | | | | | | | (10 elements of type int *)
     - - - - - - - - - - 
     | |     ....      |     _ _ _ _ _ _
     | |                \-->| | | | | | | (6 elements of type int)
     | |                     - - - - - -
     | |
     | |      _ _ _ _ _ _
     |  \ -->| | | | | | | (6 elements of type int)
     |        - - - - - -
     |
     |
     |      _ _ _ _ _ _
      \ -->| | | | | | | (6 elements of type int)
            - - - - - -
    

    当你说array[x][y]时,它会翻译成*((int *)array+x*6+y)

    虽然,当您说 array2[x][y] 时,它会转换为 *(*(array2+x)+y)(请注意,对于 array,此公式也适用(阅读到帖子末尾,然后是 cmets)。

    也就是说,静态二维数组实际上是一维数组,其中行放在一行中。该指数由公式row * number_of_columns_in_one_row + column计算得出。

    动态二维数组,然而只是一个指针的一维数组。然后动态分配每个指针以指向另一个一维数组。事实上,那个指针可以是任何东西。可能是NULL,或者指向单个变量,或者指向另一个数组。而且这些指针中的每一个都是单独设置的,因此它们可以具有不同的性质。

    如果您需要在某处传递array 的指针,则不能将其转换为int **(想象一下会发生什么。array 的单元格的int 值被解释为指针并取消引用-> Bam!分段错误!)。但是,您可以将array 视为int [6]s 的一维数组;这是一维元素数组,类型为int [6]。要写下来,你说

    int (*p)[6] = array;
    

    【讨论】:

    • 如果array 分配在内存中,如您所描述的,something like this 仍然可以工作吗?我可以理解array[x][y] 转换为*(((int *)array)+x*6+y),但为什么*(*(array + i) + j) 仍然有效?
    • @KohányiRóbert,这是一个很好的观点。事实上,对于一个(非动态)多维数组(在这种情况下为array),两者是等价的。需要注意表达式中array的类型才能理解。首先,请注意在*(((int *)array)+x*6+y) 中,我首先将array 转换为int *。这实质上意味着x*6+yints 数组的索引。
    • 现在当您执行*(*(array + i) + j)) 时,您不会更改array 的类型。 array 的类型是什么?这是int (*)[6]sizeof 是什么?这是sizeof(int) * 6。现在*(array + i)(int[6])((int *)array + i*6) 相同(有意义吗?)。用 j 索引它变成*((int *)array + i*6 + j)
    • int *p[6] = array; 也有效吗?括号对编译器有影响吗?
    • @Rohan,括号很重要。 int *p[6] 是一个包含 6 个指针的数组。 int (*p)[6] 是一个指向 6 个整数数组的指针。这与函数指针非常相似。 int *f()int (*f)() 是不同的东西。
    【解决方案3】:

    在 C 中,二维数组是数组的数组

    你需要一个指向数组的指针来引用它,而不是双指针:

    char array[2][6] = {"hello", "hai"};
    char (*p)[6] = array;
    //char **x = array;  // doesn't compile.
    

    对于引用“二维数据”的双指针,它必须引用指针数组的第一个元素。但是C中的二维数组(array of arrays)和指针数组不是一回事,如果只定义一个二维数组,那么就不存在对应的指针数组了。

    两者之间唯一的相似之处是用于访问数据的[][] 语法:数据本身的结构完全不同。

    【讨论】:

      【解决方案4】:

      制作一个指向每一行的指针数组以获得一个“看起来像”可变大小的多维数组的对象是为了语法糖而昂贵的设计选择。不要这样做。

      做一个可变大小的多维数组的正确方法是这样的:

      if (w > SIZE_MAX/sizeof *m/h) goto error;
      m = malloc(w * h * sizeof *m);
      if (!m) goto error;
      ...
      m[y*w+x] = foo;
      

      如果你想让它“看起来很漂亮”以便你可以写 m[y][x],你应该使用不同的语言,也许是 C++。

      【讨论】:

      • 您可以使用变长数组让编译器为您计算偏移量:int (*foo)[cols] = malloc(sizeof *foo * rows) 可以使用熟悉的foo[i][j] 语法;
      • @Christoph:确实,如果你有一个 C99 编译器,这是可行的。不过,您需要安排将尺寸正确地传递给您将数组传递给的任何函数调用。
      【解决方案5】:

      拥有指针对指针意味着每一行(或列,如果您更愿意这样想的话)可以具有与其他行/列不同的长度。

      您还可以仅通过指向起始元素的指针和指定每行/列元素数的整数来表示二维数组:

      void matrix_set(double *first, size_t row_size, size_t x, size_t y, double value)
      {
        first[y * row_size + x] = value;
      }
      

      【讨论】:

        【解决方案6】:

        让我们从法律法规开始。你写的(假设每个声明前面都有一个 char)不会编译,原因有几个:你有太多的初始化程序(arr[0] 有六个 char,它的大小是 5),当然还有 char ** p 没有与 char arr[2][5] 兼容的类型。纠正这些问题,我们得到:

        char arr[2][6] = { "hello", "hai" };
        char (*p)[6] = arr;
        

        没有任何双指针。如果你想访问上面的单个字符,你需要指定它们来自的元素:

        char* pc = *arr;
        

        如果您想从 arr 中的第一个元素访问字符,则可以。

        C++ 没有二维数组。上面的第一个定义定义了一个 char 的数组 [2] 或数组 [6]。隐式数组到指针的转换导致指向 char 数组 [6] 的指针。之后当然就没有数组到指针的转换了,因为你不再有数组了。

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2021-06-02
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2021-12-28
          相关资源
          最近更新 更多