【问题标题】:C programming 2D array memory layoutC 编程 2D 数组内存布局
【发布时间】:2020-03-17 10:31:03
【问题描述】:

考虑下面的 c 代码。

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

现在每个 printf 语句都打印出与下面提到的相同的值。

6356724, 6356724, 6356724
6356688, 6356688, 6356688
Process returned 0 (0x0)   execution time : 0.128 s
Press any key to continue.

我想知道每个 printf 语句的结果如何相同。 这是我对二维数组内存布局的理解。

现在我相信 x+3 指的是(x + 3 * 指针运算所需的大小)= 24 的基地址 和 *(x +3 ) = *(24) = 2036 但不是第一个 printf 语句打印 2036, 2036 2036 的情况。我想直观地了解二维数组是如何组织的。

【问题讨论】:

  • 如果您尝试打印指针值,请使用 %p 而不是 %u
  • @Eraklon 它以十六进制格式打印地址:0060FEF4, 0060FEF4,0060FEF4 0060FED0, 0060FED0, 0060FED0 进程返回 0 (0x0) 执行时间:0.119 s 按任意键继续。
  • x + 3x[3] 相同,也与*(x + 3) 相同。 *(x + 2) + 3x[2] + 3,因为数组是 3 宽,因此这个加法基本上返回 x[3]。鉴于机器上的 int 是 4 字节大小,您对内存布局的理解似乎是正确的。混乱来自指针算术。
  • @Eraklon "x + 3x[3] 相同" ???
  • 这段代码的行为是未定义的。它可以打印任何东西,或者什么都不打印,或者融化你的硬盘。

标签: c arrays multidimensional-array data-structures


【解决方案1】:

每当提到一个数组时,它decays 指向一个指向它的第一个元素的指针(除了现在不感兴趣的少数情况)。

为什么x&amp;x*x 都打印相同?这是因为数组本身,它的第一个元素,(如果它是一个二维数组,它的第一个元素的第一个元素,它本身就是一个一维数组)都具有相同的地址。

所以:

  • &amp;x 是指向数组的指针
  • x 是数组本身,它衰减到它的第一个元素的指针
  • *x是数组的第一个元素,它本身就是一个数组(1, 2, 3},所以它衰减到它的第一个元素的指针

所有这些指针都有不同的类型,但指向同一个地址。

x+3*(x+3) 也会发生同样的情况。首先,x 衰减为指针,然后执行指针运算。说y = x + 3,所以y*y 指向同一个地址,就像x 和*x` 一样(见上文)。

*(x+2)+3 呢?好吧,*(x + 2) 本身就是一个由 3 个元素组成的数组。所以*(x+2)+3 指向*(x + 2) 数组的一个元素结束。这个不存在的元素has an address nevertheless,和*(x + 3)数组的第一个元素的地址相同---因为*(x + 2)*(x + 3)x数组中是相邻的。

【讨论】:

    【解决方案2】:

    我会尝试从外行的角度来解释。

    假设基地址为 3000,假设整数占用 4 个字节。

    考虑到您的二维数组,这就是我们在内存中可视化二维数组的方式:

             0                       1                2
         --------------------------------------------------
      0 |   1                |    2           |   3          |
        | 3000/3001/3002/3003| 3004/05/06/07  | 3008/09/10/11|
        |___________________ |________________|______________|                      
      1 |    4               |    5           |   6          |
        |   3012             |    3016        |   3020       |
        |____________________|________________|______________|                      
      2 |    7               |    8           |   9          |
        |    3024            |    3028        |   3032       |
        |____________________|________________|______________|                      
      3 |     10             |   11           |   12         |
        |    3036            |   3040         |   3044       |
        |                    |                |              |
        ------------------------------------------------------
    

    现在,您应该知道二维数组是如何像内存中的一维数组一样实际存储的:

    index    0       1       2      3     4      5     6      7      8      9      10    11  
    
            ---------------------------------------------------------------------------------
           |  1   |  2   |  3   |  4   |  5   | 6   |  7   |  8   |  9   |  10  |  11 |  12  |       
           |      |      |      |      |      |     |      |      |      |      |     |      |
    add.   |3000__|_3004_|_3008_|_3012_|_3016_|_3020|3024__|_3028_|3032__|_3036_|_3040|3044_ |
    

    现在,您的代码显示:

    printf("%u, %u, %u", x+3, *(x+3), *(x+2)+3);
    

    (i) x+3

    这里的 x 是你的基地址,即 3000(假定),+3 表示第 3 行。由于之后没有任何内容,即 x+3,我们将找出第 3 行第一个元素的地址。

    =3000+36=3036

    (参考我们看到的二维数组可视化,第一个)

    (ii) *(x+3)

    这与 x+3 相同,即我们必须找到第三行的第一个元素的地址。我说第一个元素是因为如果在 *(x+3)+'something' 之后提到了什么,那么我们会担心找到它。

    =3000+36=3036。

    (参考我们看到的二维数组可视化,第一个)

    (iii) *(x+2)+3

    只是为了理解,让我们把它分成两部分。

    首先,*(x+2):正如我们在上面两部分中看到的,*(x+2) 表示“我已经从基地址开始,现在给我第一个元素的地址第二行。所以,我们到达值为 7 且地址为 3024 的元素(参考我们看到的 2D 数组可视化,第一个)

    其次,*(x+2)+3 中的 +3 表示由于我们已经到达值为 7 且地址为 3024 的元素,现在查看/移动到元素之后的第三个元素值 7 和地址 3024(我们现在所在的位置)。因此,我们移动值 8 和 9 的过去/透视元素,地址分别为 3028 和 3032,并到达/降落在值 10 和地址的元素上

    =3000+36=3036

    (参考我们看到的二维数组可视化,第一个)



    第二个代码:

     printf("\n%u, %u, %u", x,&x,*x);
    

    我选择@n. 'pronouns' m. 回答。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2022-01-24
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2019-05-02
      • 1970-01-01
      • 2010-10-03
      相关资源
      最近更新 更多