【问题标题】:2D array vs array of arrays二维数组与数组数组
【发布时间】:2011-12-18 11:21:52
【问题描述】:

二维数组和数组数组有什么区别?

我读过 cmets,例如 @Dave's,似乎可以区分两者。

如果他使用 2d 数组或指向数组的指针类型而不是数组数组,这将中断。 ——戴夫

我一直以为都是指:

int arr_arr[][];

编辑: @FutureReader,你不妨看看How do I use arrays in C++?

【问题讨论】:

  • ...希望我没有打扰到你,@FutureReader。
  • 如果你想从编译器中得到更合理的警告或快速检测错误,总是更喜欢数组而不是指针。

标签: c++ c multidimensional-array


【解决方案1】:

这里有四个不同的概念。

  • 二维数组: int arr[][]。它不能在任何方向上调整大小,并且是连续的。索引它与((int*)arr)[y*w + x] 相同。必须静态分配。
  • 指向数组的指针: int (*arr)[]。它只能调整大小以添加更多行,并且是连续的。索引它与((int*)arr)[y*w + x] 相同。必须动态分配,但可以释放free(x)
  • 指针到指针: int **arr。它可以在任何方向调整大小,不一定是方形的。通常是动态分配的,不一定是连续的,释放取决于它的构造。索引与*(*(arr+y)+x) 相同。
  • 指针数组: int *arr[]。它只能调整大小以添加更多列,并且不一定是方形的。调整大小和释放也取决于构造。索引与*(*(arr+y)+x) 相同。

每一个都可以使用arr[y][x],导致混乱。

【讨论】:

  • 一个二维数组可以像任何其他类型的对象一样被分配,要么自动(“在堆栈上”,即声明为本地对象),要么动态(通过malloc()) ,或静态(使用static 关键字或在任何函数之外定义)。我认为您的意思是大小在编译时是固定的——尽管对于 C99 可变长度数组,这也不完全正确。
  • 严格来说只有第一个是二维数组;其他的是可以模拟二维数组的数据结构。但是对于所有四种形式,一旦分配了所有内容(这可能很棘手),您就可以使用相同的arr[x][y] 语法来访问各个元素。区别在于每个[] 运算符的前缀是指针表达式还是衰减 为指针的数组表达式。有4种组合。 (对于 3-d 类数组结构,有 8 种组合;对于 N 维,有 2**N。)
  • 你将如何分配一个带有malloc() 的二维数组?为此,您需要一个指向数组的指针。
  • 是的,你需要一个指向数组的指针。 int (*p)[10][10] = malloc(sizeof *p);。或者,也许更清楚:typedef int matrix[10][10]; matrix *p = malloc(sizeof *p);.
【解决方案2】:

二维数组定义为数组的数组。

Dave 的意思是,在这种情况下,二维数组的定义之间存在不同的语义,如下所示:

int x[][];

这个:

int *x[];

或者这个:

int **x;

【讨论】:

  • 但是为什么人们暗示这两者是异类的呢?
  • 有时您可能会看到定义为int (*x)[10]; 的二维数组,这是将平面数组转换为可用[x][y] 而不是[x * width + y] 索引的二维数组的好方法。
  • @muntoo:“异构”是什么意思?你的意思是“同义词”吗?
  • @KeithThompson 不,我的意思是 SO 上的某些人暗示 2D array != array of arrays。 (不仅仅是戴夫。)最初,这个答案中只有第一行存在;其他的东西后来被编辑了。
  • @muntoo:好的。我认为“不同”会更清楚地表达这一点。
【解决方案3】:

这里的答案有点微妙。

数组数组的定义如下:

int array2[][];

指向数组的指针类型定义为:

int (*array2)[];

指针数组类型定义为:

int* array2[];

编译器对这两者的处理方式略有不同,实际上还有一个选择:

int** array2;

很多人都被告知这三个是相同的,但是如果您对编译器了解更多,您肯定会知道差异很小,但确实存在。如果您将一个程序替换为另一个程序,很多程序都会运行,但在编译器和 ASM 级别,情况并不相同。一本关于 C 编译器的教科书应该提供更深入的答案。

此外,如果有人对二维数组的实现感兴趣,则有多种方法在效率上有所不同,具体取决于具体情况。您可以将 2D 数组映射到 1D 数组,从而在处理线性化数据时确保空间局部性。如果您想要易于编程,并且如果您需要单独操作行/列,则可以使用数组数组。有某些阻塞类型和其他奇特的设计是缓存智能的,但如果你是用户,你很少需要知道实现。

希望我能帮上忙!

【讨论】:

  • 不,这是一个指针数组。 int (*array)[n] 是一个指向数组的指针。
  • 差别不是“很小”。这是数组和指针之间的区别,这是一个非常重要的区别(也是太多 C 程序员忽略的区别)。
  • @KeithThompson:如果有这么多 C 程序员可以有效地使用这门语言,同时又忽略了它,那么区别会有多重要?
  • @NicolBolas:我不相信他们确实有效地使用了它。不了解区别会使某些类型的错误变得非常容易。
  • 感谢 cmets,我编辑了答案以提供更多深度并修复了指向数组的错字。
【解决方案4】:

下面是一个二维数组,可以称为数组数组:

int AoA[10][10];

以下是指向已设置为用作二维数组的指针的指针:

int **P2P = malloc(10 * sizeof *P2P);
if(!P2P) exit(1);
for(size_t i = 0; i < 10; i++)
  {
    P2P[i] = malloc(10 * sizeof **P2P);
    if(!P2P[i])
      {
        for(; i > 0; i--)
            free(P2P[i - 1]);
        free(P2P); 
      }
  }

两者都可以通过AoA[x][y]P2P[x][y] 访问,但两者不兼容。特别是,P2P = AoA 是新手有时期望工作但不会工作的东西 - P2P 期望指向指针,但是当 AoA 衰减为指针时,它是一个指针指向数组 em>,特别是int (*)[10],这不是P2P 应该是的int **

【讨论】:

    【解决方案5】:

    二维数组可以包括:

    int x[width * height]; // access: x[x + y * width];
    

    来自维基百科:

    对于二维数组,索引为 i,j 的元素将有 地址 B + c · i + d · j,其中系数 c 和 d 是行 和列地址分别递增。

    【讨论】:

    • 或者如果你想炫耀,int arr_[width * height]; int (*arr)[width] = (void *)arr_; arr[x][y] = 0;
    • 那不是真正的二维数组;它是一维数组(可用于模拟二维数组)。
    • @KeithThompson 实际上是相反的。这是二维数组的最真实形式,数组中的数组是一种表示形式。 en.wikipedia.org/wiki/Array_data_structure
    • @Pubby8:你错了。您引用的维基百科文章实际上并不支持您的主张。 C standard 的第 6.5.2.1 节将数组的数组称为多维数组。
    • x[7*3] 的索引与x[3*7] 的索引相同,因此您只存储了一半可能的数据,并且会丢失另一半或覆盖前半部分。所以考虑像这样使用它x[7*widthIndexlength+3]
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2010-11-17
    • 2013-05-19
    • 1970-01-01
    • 2014-04-29
    • 2010-09-06
    相关资源
    最近更新 更多