【问题标题】:To what does a 2D array points to?二维数组指向什么?
【发布时间】:2021-11-29 07:19:00
【问题描述】:

这可能是一个愚蠢的问题,但我正在读一本书,他们向我泼了这段代码

int arr[6][8];
int *ptr = &arr[0][0];
*(ptr+8) = 12; // arr[1][0]
printf("%d\n", arr[1][0]);

现在...我明白为什么这个指针指向那个单元格了,但是如果我必须使用 arr 标识符,我应该用这种方式来代替

*(*(arr+1)) = 12; // arr[1][0]

但我不明白为什么我不能这样说

*(*(arr+8)) = 12;

也许我对指针和二维数组的了解很差,但我真的很想了解这些东西是如何工作的。

提前感谢您的回答!

【问题讨论】:

  • *(arr+8) = 12; // arr[1][0] 不正确。在 C 中,*(*(arr + i) + ja[i][j] 处引用int。 C 已经知道元素和嵌套数组的长度,并将这些大小考虑在内。
  • 我错了,我拼错了,我的意思是 *(ptr+8) 而不是 *(arr+8)
  • 然而,当我尝试读取存储的值时,它给了我 4
  • 当你读取什么值?您的代码没有显示您是如何阅读它的。如果您正在阅读*(ptr+8),那么您正在阅读数组之外的一些值,或者内存中的其他地方,无论是什么。可能在堆栈中更靠前的东西不会导致访问冲突。
  • 再次编辑,我正在阅读 arr[1][0] 处的元素

标签: c pointers multidimensional-array


【解决方案1】:

一个数组,无论是一维、二维还是更多,都不指向任何东西。它在连续内存中的一组相同类型的对象。

int arr[6][8]; 之后,arr 是一个由 8 个 int 组成的 6 个数组的数组。如果每个int 使用四个字节,那么整个数组在内存中使用 6•8•4 = 192 字节,sizeof arr 将计算为 192,因为arr 是数组,并且sizeof 给出其操作数的大小。

当您在表达式中使用arr 时,它将自动转换为指向其第一个元素的指针,除非它是sizeof 或一元& 的操作数。 1 因为arr是一个数组数组,所以它的第一个元素也是一个数组。 arr的第一个元素是arr[0],是一个8个int的数组。因此,在arr+1 中,arr 被转换为指向其第一个元素的指针。那个指针是&arr[0]arr[0]的地址。

当一个整数,比如 n 被添加到一个指针上时,结果指向数组中另外 n 个元素。因此,当我们将 1 加到 &arr[0] 时,我们得到 &arr[1]。因此,在arr+1中,arr自动转换为&arr[0],产生&arr[0]+1,然后相加产生&arr[1]

所以arr+1就是&arr[1],这也是arr[1][0]所在的地方。 (请注意,虽然&arr[1]&arr[1][0] 指向内存中的同一位置,但它们具有不同的类型,因此编译器在对它们进行算术运算时会以不同的方式处理它们。)

int *ptr = &arr[0][0];之后,ptr当然指向arr[0][0]。然后ptr+8 指向arr[0][8] 的位置,如果有这样的元素。当然,没有,因为arr[0] 只有从arr[0][0]arr[0][7] 的元素。 ptr+8arr[0][7] 高一分。

指针算法是由 C 标准定义的;您可以指向“最后一个元素之外的一个”。然而,这只是一个占位符指针,对算术和比较有用。使用 *(ptr+8) 取消引用指针时不会发生 C 标准。

我们知道arr[1][0] 位于假设arr[0][8] 所在的位置。然而,C 标准并没有给我们规定说我们绝对可以使用*(ptr+8) 访问arr[1][0]。因此该代码没有 C 标准定义的行为。不过,许多编译器会认为它可以访问arr[1][0]

如您所述,*(*(arr+1)) 可用于访问arr[1][0]。它的工作方式是:

  • arr 会自动转换为 &arr[0]
  • 加 1 得到&arr[1]
  • * 取消引用 &arr[1],生成 arr[1]
  • arr[1] 是一个数组,因此它会自动转换为指向其第一个元素的指针。这会产生&arr[1][0]
  • * 取消引用 &arr[1][0],生成 arr[1][0]

相比之下,*(*(arr+8)) 无法访问arr[1][0]

  • arr 会自动转换为 &arr[0]
  • 加 8 得到&arr[8]

现在我们已经远远超出了arr 的终点。 arr 仅包含从 arr[0]arr[5] 的元素,因此 arr[8] 超出了结尾。

请注意,将 8 添加到 &arr[0] 会将指针移动 arr 数组的 8 个元素,而不是 arr[0] 数组的 8 个元素。也就是说,它移动了 8 个数组,每组 8 个 int,而不是 8 个 int。这是因为&arr[0]type 是“指向8 个数组的指针int”,而不是“指向int 的指针”。

将 8 添加到“指向int”的指针时,会将其移动 8 int。当您将 8 添加到“指向 8 个int 的数组的指针”时,您将其移动了 8 个由 8 个int 组成的数组。

脚注

1 所有数组都是这样自动转换的,字符串字面量还有一个例外。当使用字符串字面量(即字符数组)初始化字符数组时,它不会自动转换为指针。

【讨论】:

    【解决方案2】:
    *(*(arr+8)) = 12;
    

    翻译成

    arr[8][0] = 12;
    

    超出数组范围。写入数组边界外的行为是未定义的 - 它可能会立即崩溃,它可能会破坏堆栈中的数据,或者它可能会在没有任何明显问题的情况下工作。

    请记住,除非它是 sizeof 或一元 & 运算符的操作数,否则“T 的 N 元素数组”类型的 表达式 会被转换或“衰减” , 类型为“指向T”的表达式,表达式的值是数组第一个元素的地址。表达式arr 的类型为“int 的 8 元素数组的 6 元素数组”;在大多数情况下,它“衰减”为“指向int 的 8 元素数组的指针”或int (*)[8]

    给一个指针值加 1 会产生一个指向下一个被指向类型的 object 的指针。如果表达式arr 产生一个指向int 的8 元素数组的指针,那么表达式arr + 1 产生一个指向紧随其后的下一个8 元素数组int 的指针.因此

          int          int *        int (*)[8]
    +–––+ 
    |   | arr[0][0]    p            arr
    +–––+      
    |   | arr[0][1]    p+1 
    +–––+
    |   | arr[0][2]    p+2
    +–—–+
     ...
    +–—–+
    |   | arr[0][6]    p+6
    +–––+
    |   | arr[0][7]    p+7
    +–––+
    |   | arr[1][0]    p+8          arr+1
    +—––+
    

    这就是为什么*(*(arr + 8)) == arr[8][0] - arr + 8arr 之后产生指向int 的第8 个8 元素数组的指针。这就是为什么*(*(arr + 1)) == arr[1][0]

    【讨论】:

      猜你喜欢
      • 2017-04-22
      • 2018-01-06
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2012-01-26
      相关资源
      最近更新 更多