这是我的看法:
C语言的发展为C中数组类型的演变提供了一些见识:
我将尝试概述数组的内容:
C的先行者B和BCPL没有明显的数组类型,声明如下:
auto V[10] (B)
or
let V = vec 10 (BCPL)
将声明V为一个(无类型的)指针,该指针被初始化为指向内存的10个“单词”的未使用区域。就像今天的C / C ++一样,B已经用于*
指针解引用,并且具有[]
简写的*(V+i)
含义V[i]
,即。但是,V
它不是数组,它仍然是必须指向某些内存的指针。当Dennis Ritchie尝试用结构类型扩展B时,这引起了麻烦。他希望数组成为结构的一部分,就像今天的C语言一样:
struct {
int inumber;
char name[14];
};
但是使用B,BCPL数组作为指针的概念,这将要求name
字段包含一个指针,该指针必须在运行时初始化为该结构内14个字节的内存区域。最终,通过对数组进行特殊处理来解决初始化/布局问题:编译器将跟踪数组在结构中,在堆栈等上的位置,而无需实际实现指向数据的指针(涉及数组的表达式除外)。这种处理使几乎所有B代码仍然可以运行,并且是“如果您看一下数组,数组就会转换为指针”规则的来源。这是一个兼容性黑客,事实证明它非常方便,因为它允许数组具有开放大小等。
这就是我为什么不能分配数组的猜测:由于数组是B中的指针,因此您可以简单地编写:
auto V[10];
V=V+5;
重新设定“数组”的基础。现在这已经没有意义了,因为数组变量的基不再是左值。因此,这种分配是不被允许的,这有助于抓住一些在声明的数组上进行重新引用的程序。然后这个想法就陷入了僵局:由于数组从未被设计为C类型系统的一等奖,因此它们大多被当作特殊的野兽对待,如果使用它们,它们会变成指针。从某种角度(忽略了C数组是一个烂摊子)来看,禁止分配数组还是有一定道理的:将开放数组或数组函数参数视为没有大小信息的指针。编译器没有信息来为其生成数组分配,并且出于兼容性原因需要使用指针分配。
typedef int vec[3];
void f(vec a, vec b)
{
vec x,y;
a=b;
x=y;
a=x;
x=a;
}
1978年C的修订版增加了结构赋值(http://cm.bell-labs.com/cm/cs/who/dmr/cchanges.pdf)时,情况并没有改变。即使记录在C中是不同的类型,也无法在早期K&R C中进行分配。您必须使用memcpy逐成员复制它们,并且只能将指向它们的指针作为函数参数传递。现在,将赋值(和参数传递)简单地定义为该结构的原始内存的memcpy,由于这不会破坏现有代码,因此可以很容易地对其进行分配。作为意外的副作用,这隐式引入了某种数组分配,但是这发生在结构内部的某个位置,因此这实际上并不会引入数组使用方式的问题。