为什么数组名称是指向数组第一个元素的指针?

时间:2013-08-29 18:17:31

标签: c arrays pointers

总是这样,我的意思是,数组名称总是指向数组的第一个元素的指针。为什么呢?它是实现有点事物还是语言特征?

3 个答案:

答案 0 :(得分:15)

数组名称本身不是指针,但在大多数上下文中衰减为指向数组第一个元素的指针。就是这样,因为语言就是这样定义的。

来自C11 6.3.2.1 Lvalues,数组和函数指示符,第3段:

  

除非是 sizeof 运算符的操作数, _Alignof 运算符或一元 {{1} } 运算符,或者是用于初始化数组的字符串文字,类型为“ type ”的表达式转换为类型为“指向类型<的指针的表达式” / em>“指向数组对象的初始元素,而不是左值。

您可以从Arrays and Pointerscomp.lang.c FAQ部分了解有关此主题的更多信息(以及涉及的微妙行为)。

编辑抛开:同样的行为发生在C ++中,尽管语言指定它有点不同。作为参考,我在这里有一个C ++ 11草案, 4.2数组到指针转换,第1段:

  

& N数组”或“T未知范围数组”的左值或右值可以转换为“指向{{1的指针”的右值}}”。结果是指向数组的第一个元素的指针。

答案 1 :(得分:8)

可以找到此行为的历史原因here

C源自一个名为B的早期语言(go figure)。 B是无类型语言,内存被视为“单元格”的线性数组,基本上是无符号整数。

在B中,当您声明N元素数组时,如

auto a[10];

为数组分配N个单元格,并留出另一个单元格来存储第一个元素的地址,该元素绑定到变量a。与在C中一样,数组索引是通过指针算法完成的:

a[j] == *(a+j)

这很好用,直到Ritchie开始向C语言添加结构类型。他在文章中给出的例子是一个假设的文件系统条目,它是一个节点id后跟一个名字:

struct {
  int inumber;
  char name[14];
};

他希望struct类型的内容与磁盘上的数据相匹配;一个整数的2个字节,紧接着是14个字节的名称。没有把指针存放到数组的第一个元素的好地方。

所以他摆脱了它。他没有为指针设置存储空间,而是设计了语言,以便从数组表达式本身计算指针值。

顺便说一下,这就是为什么数组表达式不能作为赋值的目标;它与写3 = 4;实际上是一回事 - 你试图将值赋给另一个值。

答案 2 :(得分:2)

Carl Norum已经就这个问题给出了语言律师的答案(并得到了我的支持),这里有实施细节答案:

对于计算机,内存中的任何对象只是一个字节范围,就内存处理而言,由第一个字节的地址和字节大小唯一标识。即使你的内存中有int,它的地址也不过是第一个字节的地址。大小几乎总是隐式的:如果将指针传递给int,编译器就知道它的大小,因为它知道该地址的字节将被解释为int。结构也是如此:它们的地址是第一个字节的地址,它们的大小是隐含的。

现在,语言设计人员可以使用与结构一样的数组来实现类似的语义,但是他们没有充分的理由:与传递指针相比,复制效率比现在更低效,结构已经在大多数情况下使用指针传递,并且数组通常意味着很大。通过语言对它们强制施加价值语义是非常大的。

因此,通过指定数组的名称实际上等于指针,数组只是被迫成为内存对象。为了不打破数组与其他内存对象的相似性,大小再次被认为是隐式的(对于语言实现,而不是程序员!):编译器在传递时可能会忘记数组的大小在其他地方,依靠程序员知道,数组中有多少个对象。

这样做的好处是阵列访问非常简单;它们衰减到指针算术的问题,将索引与数组中对象的大小相乘,并将该偏移量添加到指针。这就是a[5]5[a]完全相同的原因,它是*(a + 5)的缩写。

另一个与性能相关的方面是从阵列制作子阵列非常简单:只需要​​计算起始地址。没有任何东西会迫使我们将数据复制到一个新的数组中,我们只需要记住使用正确的大小......

所以,是的,它在实现简单性和性能方面有深刻的理由,数组名称会以它们的方式衰减,我们应该为此感到高兴。