【问题标题】:2-D Arrays and pointers二维数组和指针
【发布时间】:2019-09-09 10:34:51
【问题描述】:

有人告诉我,编译器会在必要时将数组名称转换为指向数组第一个元素的指针。所以我用这个规则解决了很多问题,直到我看到这个都很好。

int main()
{
    int a[2][3] = {{1,2,3}, {5,6,7}}, (*p)[3];
    p = a[0];
    for(int i = 0; i < 3; i++)
        printf("%d", (*p)[i]);
}

输出是123,带有代码块。

我试图解决它:

(*p)[i]) 应与*((*p)+i)....(1) 相同

正如规则所说,“数组名称被转换为指向数组第一个元素的指针”。我将第二条指令p=a[0] 转换为p=&amp;a[0][0] (因为a[0] 是一个子-数组名称,其第一个元素是a[0][0])。 所以(*p) 可以写成(*(&amp;a[0][0]))。与a[0][0] 相同。所以 eqn (1) 变成了

*(a[0][0]+i). 

这就是我卡住的地方。 a[0][0]+i 是一个元素而不是地址。所以对它应用* 操作符是无效的。但我也在原始代码中用p=&amp;a[0][0]; 替换了声明p=a[0];,答案保持不变。我也将其替换为p=&amp;a[0];。答案还是一样。现在我超级困惑。

【问题讨论】:

  • 如果你编译程序,你将得到类似 warning: assignment from incompatible pointer type [-Wincompatible-pointer-types] p=a[ 0] ;,这就解释了为什么它没有意义。
  • 你被教导正确C11 Standard - 6.3.2.1 Other Operands - Lvalues, arrays, and function designators(p3) --- 你的实现是错误的,而不是p=a;。启用编译器警告(-Wall -Wextra -pedantic 用于 gcc/clang)和(/W3 用于 VS)并且在编译没有警告之前不要接受代码。 a[0]int *,但是 pint (*)[3]。由于a 将衰减为指向访问时第一个元素的指针(即3 int 的数组),ap 兼容,而不是a[0]
  • @AnttiHaapala 是的,它产生了警告。但是为什么这不是编译错误?这是否意味着编译器已经自行将其转换为兼容类型?如果这个代码是在笔试中给我的,我应该将错误标记为我的正确选项吗?
  • 不是编译错误,因为the C standard explicitly allows a compiler to successfully compile an invalid program, provided that required diagnostics are issued。这是为了允许使用 C 编译器来编译 1970 年代以来的各种 b0rken 代码等等......
  • 太棒了,学习已经发生了——这让今天很美好......

标签: c pointers multidimensional-array array-formulas pointer-arithmetic


【解决方案1】:

二维数组是一维数组的数组

在一维数组中,

a[0] => *(a)     -> first element of array
a[1] => *(a + 1) -> second element of array

在二维数组中,

a => the adress of first array
a[0] => *(a)        -> the first array 
a[0][0] => *(*(a))     -> the first element of first array
a[0][1] => *(*(a) + 1) -> second element of first array
a[1][1] => *(*(a + 1) + 1) -> second element of second array

【讨论】:

  • 不完全。 a[2][3] 在访问时衰减为 (*a)[3]指向 int [3] 的指针),a[0](即 int [3])在访问时衰减为 int *访问(指向 int 的指针)。 C11 Standard - 6.3.2.1 Other Operands - Lvalues, arrays, and function designators(p3)
  • 澄清一下,您的指针/索引等价物是正确的(尽管在倒数第二个缺少一个结束括号),问题是"2d arrays are array of pointer of pointers."。数组不是指针,指针也不是数组,它们是独立且不同的类型,即使数组在访问时将转换为指向它的第一个元素的指针。
  • 是的,抱歉信息有误。可能最正确的定义就像2d arrays are array of pointer of arrays.,因为它们不是内存中的连续元素。如果我错了,你能纠正我吗?
  • 没问题,没问题,我知道你的意图,但新的或在这个概念上苦苦挣扎的人不会。最好把语言整理一下。从技术上讲,二维数组是一维数组的数组,指针只有在第 6.3.2.1 节的应用中才会出现。
  • @1v3m:没有“二维数组是数组指针的数组”不是正确的定义。 int a[2][3]是2个数组的数组int,所有元素都是连续的。不涉及指针。
【解决方案2】:

此行不正确:

p = a[0];

p 的类型是指向 3 个数组 int 的指针。 a[0] 的类型是指向int 的指针。你的编译器应该给你一个警告。如果没有,请启用任何必要的开关来打开大部分或全部编译器警告。当编译器发出警告时,不要忽略它——确保在继续之前了解编译器发出警告的原因。启用将警告转化为错误的编译器功能是个好主意。

正确的说法是p = a;p = &amp;a[0];。但是,让我们检查一下您的程序中发生了什么。在表达式a[0]

  • 根据下标运算符的定义,a[0] 等价于*(a+0)(技术上是(*((a)+(0))),但在这种情况下我们不需要额外的括号)。
  • a 是一个数组,因此它被转换为指向其第一个元素的指针。所以a 被转换为指向a[0] 的指针。所以表达式现在是*(&amp;a[0] + 0)
  • 0添加到&amp;a[0]不会改变它,所以结果是&amp;a[0],现在的表达式是*&amp;a[0]
  • * 应用于指针会产生指向的事物,因此表达式变为a[0]
  • a[0] 是一个由 3 个 int 组成的数组。由于它是一个数组,因此它被转换为指向其第一个元素的指针。所以结果实际上是&amp;a[0][0]

注意&amp;a[0][0] 是指向int 的指针,正如我在上面所写的。所以现在我们看到p = a[0]; 在右侧有一个指向int 的指针,但在左侧有一个指向3 个int 的数组的指针。这违反了 C 标准中的约束,编译器必须为其发出诊断消息。

在警告之后,您的编译器可能会转换指针,导致 p 被设置为指向 a[0]

我将第二条指令 p=a[0] 转换为 p=&amp;a[0][0]因为 a[0] 是一个子数组名称,其第一个元素是 a[0][0])。所以(*p)可以写成(*(&amp;a[0][0]))

这是一个错误。尽管您执行了分配p = a[0],但这并未将&amp;a[0][0] 分配给p,并且p 的行为不像&amp;a[0][0]。分配转换 &amp;a[0][0]p 的类型。 p 仍然是它声明的类型,一个指向 3 int 的数组的指针。实际上,p&amp;a[0] 的值结束。

现在我们可以评估(*p)[i]

  • p&amp;a[0],所以表达式变成(*&amp;a[0])[i])
  • *&amp;a[0]a[0],所以表达式变为a[0][i]
  • 从那里,我们可以继续进行通常的表达式求值,很明显,结果是元素 i of element 0 of a

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2021-12-17
    • 2018-04-18
    • 2021-11-13
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多