【问题标题】:How are 3D arrays stored in C?3D 数组如何存储在 C 中?
【发布时间】:2011-08-20 17:27:02
【问题描述】:

我了解 C 中的数组是按行优先顺序分配的。因此,对于 2 x 3 数组:

0  1
2  3
4  5

存储在内存中

0 1 2 3 4 5

但是,如果我有一个 2 x 3 x 2 数组怎么办:

0  1
2  3
4  5

6  7
8  9
10 11

这些是如何存储在内存中的?就像是连续的:

0 1 2 3 4 5 6 7 8 9 10 11

还是其他方式?还是取决于什么?

【问题讨论】:

  • 问题中的第一个数组实际上是 3x2,例如int a[3][2];
  • @Alexey:这很有趣——也许这就是我困惑的根源。 C 中的数组是否应该声明为array[ny][nx],其中nynx 是y 和x 方向的元素数。此外,这是否意味着我的 3D 数组应该声明为 array[nz][ny][nx]

标签: c arrays low-level


【解决方案1】:

所有“维度”都连续存储在内存中。

考虑

    int arr[4][100][20];

您可以说arr[1]arr[2]int[100][20] 类型)是连续的
或者 arr[1][42]arr[1][43]int[20] 类型)是连续的
或者arr[1][42][7]arr[1][42][8]int 类型)是连续的

【讨论】:

    【解决方案2】:

    假设您有一个数组char arr[3][4][5]。它是一个由 3 个数组组成的数组,由 4 个数组组成,每组 5 个字符。

    为简单起见,假设arr[x][y][z] 中的值为xyz,而arr[1][2][3] 中我们存储123

    所以内存中的布局是:

      |  00  01  02  03  04  05  06  07  08  09  10  11  12  13  14  15  16  17  18  19
    --+--------------------------------------------------------------------------------   
    00| 000 001 002 003 004 010 011 012 013 014 020 021 022 023 024 030 031 032 033 034 
    20| 100 101 102 103 104 110 111 112 113 114 120 121 122 123 124 130 131 132 133 134 
    40| 200 201 202 203 204 210 211 212 213 214 220 221 222 223 224 230 231 232 233 234
    

    arr[0]arr[1]arr[2] 一个接一个出现,但每个元素的类型都是char[4][5](即表格中的三行)。

    arr[x][0] - arr[x][3]也接踵而至,其中每个元素都是char[5]类型(即表格中每一行的四个部分,000-004是arr[0][0]的一个元素)

    arr[x][y][0] - arr[x][y][4] 是一个接一个的 5 个字节。

    【讨论】:

      【解决方案3】:

      我想你已经回答了你自己的问题。多维数组以行优先顺序存储。

      参见 ANSI C 规范第 3.3.2.1 节(还有一个具体示例):

      连续的下标运算符指定一个成员 多维数组对象。如果 E 是一个 n 维数组 ( n =2) 尺寸为 i x j "x ... x" k ,然后是 E(用作非 lvalue) 被转换为指向 (n -1) 维数组的指针 尺寸 j "x ... x" k 。如果一元 * 运算符应用于此 指针显式地或隐式地作为下标的结果, result 是指向的 ( n -1) 维数组,它本身是 如果用作左值以外的其他值,则转换为指针。它跟随 由此数组以行优先顺序存储(最后一个下标 变化最快)。

      对于你的例子,你可以试试看 - http://codepad.org/10ylsgPj

      【讨论】:

        【解决方案4】:

        在低层次上,没有多维数组这样的东西。只有一块平坦的内存,大到足以容纳给定数量的元素。在 C 中,多维数组在概念上是一个数组,其元素也是数组。所以如果你这样做:

        int array[2][3];
        

        从概念上讲,您最终会得到:

        array[0] => [0, 1, 2]
        array[1] => [0, 1, 2]
        

        这导致元素在内存中连续排列,因为array[0]array[1] 实际上并没有保存任何数据,它们只是对两个内部数组的引用。请注意,这意味着只有[0, 1, 2] 条目实际占用内存空间。如果将此模式扩展到下一个维度,您会看到:

        int array[2][3][2];
        

        ...会给你一个类似的结构:

        array[0] => [0] => [0, 1]
                    [1] => [0, 1]
                    [2] => [0, 1]
        array[1] => [0] => [0, 1]
                    [1] => [0, 1]
                    [2] => [0, 1]
        

        它继续在内存中连续排列元素(如上所述,只有[0, 1] 条目实际上占用了内存空间,其他所有内容都只是对这些条目之一的引用的一部分)。如您所见,无论您有多少维度,这种模式都会继续存在。

        只是为了好玩:

        int array[2][3][2][5];
        

        给你:

        array[0] => [0] => [0] => [0, 1, 2, 3, 4]
                           [1] => [0, 1, 2, 3, 4]
                    [1] => [0] => [0, 1, 2, 3, 4]
                           [1] => [0, 1, 2, 3, 4]
                    [2] => [0] => [0, 1, 2, 3, 4]
                           [1] => [0, 1, 2, 3, 4]
        array[1] => [0] => [0] => [0, 1, 2, 3, 4]
                           [1] => [0, 1, 2, 3, 4]
                    [1] => [0] => [0, 1, 2, 3, 4]
                           [1] => [0, 1, 2, 3, 4]
                    [2] => [0] => [0, 1, 2, 3, 4]
                           [1] => [0, 1, 2, 3, 4]
        

        【讨论】:

          【解决方案5】:

          是的,你是对的 - 它们是连续存储的。考虑这个例子:

          #include <stdio.h>
          
          int array3d[2][3][2] = {
            {{0, 1}, {2, 3}, {3, 4}},
            {{5, 6}, {7, 8}, {9, 10}}
          };
          
          int main()
          {
            int i;
            for(i = 0; i < 12; i++) {
              printf("%d ", *((int*)array3d + i));
            }
            printf("\n");
            return 0;
          }
          

          输出:

          0 1 2 3 3 4 5 6 7 8 9 10

          【讨论】:

          • 您好,我只是想知道如何将这个 3d 数组可视化为矩阵以及它是如何分配元素的?
          【解决方案6】:

          是的,它们只是按连续顺序存储。你可以这样测试:

          #include <stdio.h>
          
          int main (int argc, char const *argv[])
          {
            int numbers [2][3][4] = {{{1,2,3,4},{5,6,7,8},{9,10,11,12}}
                                    ,{{13,14,15,16},{17,18,19,20},{21,22,23,24}}};
          
            int i,j,k;
          
            printf("3D:\n");
            for(i=0;i<2;++i)
              for(j=0;j<3;++j)
                for(k=0;k<4;++k)
                  printf("%i ", numbers[i][j][k]);
          
            printf("\n\n1D:\n");
            for(i=0;i<24;++i)
              printf("%i ", *((int*)numbers+i));
          
            printf("\n");
          
            return 0;
          }
          

          这意味着对维度为 (N,M,L) 的多索引数组的访问将转换为如下所示的一维访问:

          array[i][j][k] = array[M*L*i + L*j + k]
          

          【讨论】:

            【解决方案7】:

            回答 OP 对主要问题的评论(会有点长,所以我决定回答,而不是评论):

            C 中的数组应该声明为array[ny][nx],其中nynx 是y 和x 方向的元素数。此外,这是否意味着我的 3D 数组应该声明为 array[nz][ny][nx]

            在数学中,MxN 矩阵有 M 行 N 列。矩阵元素的常用符号是a(i,j), 1&lt;=i&lt;=M, 1&lt;=j&lt;=N。所以你问题中的第一个矩阵是一个 3x2 矩阵。

            确实,它与通常用于例如的符号不同。图形用户界面元素。一个 800x600 的位图有 800 个水平像素(沿 X 轴)和 600 个垂直像素(沿 Y 轴)。如果有人想将其描述为一个矩阵,那么在数学符号中,它将是一个 600x800 矩阵(600 行,800 列)。

            现在,C 中的多维数组以这样一种方式存储在内存中:a[i][j+1]a[i][j] 旁边,而 a[i+1][j] 在 N 个元素之外。通常说“最后一个下标变化最快”,或者通常是“按行存储”:二维矩阵中的一行(即具有相同第一个索引的元素)在内存中连续放置,而一列(相同的第二个索引) 由彼此远离的元素组成。了解性能方面的考虑很重要:访问相邻元素通常要快得多(由于硬件缓存等),因此例如应组织嵌套循环,以便最里面的循环遍历最后一个索引。

            回到问题:如果您的 2D 数组的心理图像(抽象)是 Carthesian 坐标中的格子,那么是的,您可以将其视为 C 中的 array[NY][NX]。但是,如果您需要描述真实的作为数组的 2D 或 3D 数据,索引的选择可能取决于其他因素:数据格式、方便的表示法、性能等。例如,如果位图的内存表示为 array[NX][NY],则您需要使用,你会这样声明它,也许你甚至不需要知道位图变成了某种“转置”:)

            【讨论】:

              【解决方案8】:

              3d 数组是扩展的 2d 数组。

              例如我们有一个数组 - int arr(3)(5)(6);

              这是一个由两个二维数组组成的数组,其中数组将包含一个 4 行和 3 列的二维数组。

              【讨论】:

                猜你喜欢
                • 1970-01-01
                • 2016-12-23
                • 2020-08-21
                • 2012-06-28
                • 2011-12-11
                • 1970-01-01
                • 2013-04-25
                • 1970-01-01
                • 1970-01-01
                相关资源
                最近更新 更多