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

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

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

从C11 6.3.2.1左值,数组和函数指示符 ,第3段:

除非它是sizeof运算符, _Alignof运算符或一元&运算符的操作数,或者是用于初始化数组的字符串文字,否则将类型为“数组类型 ”的表达式转换为类型为“指向类型 “指向数组对象的初始元素而不是左值的指针。

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

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

可以将“ N T数组”或“ T未知数组的数组”类型的左值或右值转换为“指向T指针”的右值。 结果是指向数组的第一个元素的指针。

可以在此处找到此行为的历史原因。

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

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

 auto a[10]; 

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

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

在Ritchie开始向C语言添加结构类型之前,这一点非常有效。他在文中给出的示例是一个假设的文件系统条目,它是一个节点id后跟一个名称:

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

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

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

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

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

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

现在,语言设计者可以像使用结构那样使用数组实现类似的语义,但是他们没有充分的理由:与传递指针相比,复制的效率甚至比现在更低,结构已经传递使用了大多数时候指针,数组通常意味着很大。 通过语言对它们施加强制值语义是非常大的。

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

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

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

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