防止分配数组的理由是什么?

我试过谷歌这个并阅读:

  • 为什么不能分配相同类型和大小的数组?
  • 分配数组
  • 在c中分配给struct中的数组

但他们都说得很明显:你不能分配给数组,因为标准是这样说的。 这很好,但我想知道为什么标准不包括对分配数组的支持。 标准委员会详细讨论了一些事情,如果他们从未讨论过让arrays可分配,我会感到惊讶。 假设他们已经讨论过,他们必须有一些理由不让数组被分配。

我的意思是,我们可以将一个数组放在一个结构中并分配给结构就好了:

struct wrapper { int array[2]; }; struct wrapper a = {{1, 2}}; struct wrapper b = {{3, 4}}; a = b; // legal 

但是禁止直接使用数组,即使它有效地完成了同样的事情:

 int a[2] = {1, 2}; int b[2] = {3, 4}; a = b; // Not legal 

标准委员会禁止分配数组的理由是什么?

理解意图不是使数组表达式无法分配; 那不是目标1 。 相反,这种行为不属于Ritchie在编译器中进行简化数组处理的设计决策,但在交换中使得数组表达式成为“第二类”对象; 他们在大多数情况下失去了“arrays性”。

阅读本文 (特别是标题为“胚胎C”的部分)的某些背景; 我在这里也有更详细的答案。


1.除了Perl或PHP 2之外 ,大多数公然的语言WTF通常是设计意外或妥协的结果; 大多数语言并非刻意设计为愚蠢的。

我只是拖了一下; Perl和PHP是直接的混乱。

在C中,赋值将固定大小对象的内容复制到另一个固定大小的对象。 对于标量类型(整数,浮点,指针,自C99以来的复杂类型)实现这一定义非常明确且相当简单。 结构的分配几乎一样简单; 较大的可能需要调用memcpy()或等效的,但它仍然很简单,因为在编译时已知大小和对齐。

数组是另一回事。 大多数数组对象的大小在运行时才确定。 一个很好的例子是argv 。 运行时环境为每个命令行参数构造一个char数组,以及一个包含指向参数的指针的char*数组。 这些通过argvchar**argv的元素指向的动态分配的char[]数组可用于argv

C数组本身就是对象,但它们通常不作为对象访问。 相反,它们的元素通过指针访问,代码使用指针算法从一个元素遍历到下一个元素。

语言可以设计为将数组视为具有赋值的第一类对象 – 但它很复杂。 作为语言设计者,您必须确定10个整数的数组和20个整数的数组是否是同一类型。 如果是,您必须决定当您尝试将一个分配给另一个时会发生什么。 是否复制较小的尺寸? 它是否会导致运行时exception? 您是否必须添加切片操作以便可以对数组的子集进行操作?

如果int[10]int[20]是没有隐式转换的不同类型,那么数组操作是不灵活的(例如,参见Pascal)。

所有这些都可以定义(参见Ada),但只能通过定义比C中典型的更高级别的结构。相反,C的设计者(主要是Dennis Ritchie)选择提供具有低级操作的数组。 这无疑有时不方便,但它是一个框架,可用于实现任何其他语言的所有更高级别的数组操作。

答案很简单:在委员会介入之前从未允许过(甚至struct分配被认为太重),并考虑到arrays衰减,允许它会产生各种有趣的后果。

让我们看看会发生什么变化:

 int a[3], b[3], *c = b, *d = b; a = b; // Currently error, would assign elements a = c; // Currently error, might assign array of 3? c = a; // Currently pointer assignment with array decay c = d; // Currently pointer assignemnt 

因此,允许数组赋值将使(最多)两个当前不允许的赋值有效。

这不是麻烦,但是几乎相同的表达会产生截然不同的结果。

如果你认为函数参数中的数组符号当前只是指针的另一种表示法,那就特别激动了。
如果引入了数组赋值,那将更加令人困惑。
没有足够的人不像现在这样被事物完全混淆了……

 int g(int* x); // Function receiving pointer to int and returning int int f(int x[3]);// Currently the same. What afterwards? Change to value-copy? 

原因基本上是历史性的。 甚至在ISO C89之前有一个C,在Kernighan和Ritchie之后被称为“K&R” C。 该语言设计得足够小,因此编译器适合严格限制(按今天的标准)64kb的内存。

该语言不允许分配数组。 如果你想复制相同大小的数组, memcpy可以满足你的需求。 写memcpy(a, b, sizeof a)而不是a = b肯定不是一个很大的复杂因素。 它的另一个优点是可以推广到不同大小的数组和数组切片。

有趣的是,您提到的struct分配变通方法在K&R C中也不起作用 。您必须逐个分配成员,或者再次使用memcpy第一版 K&R的The C Programming语言提到了struct赋值作为未来语言实现的一个特性。 这最终发生在C89上。

以这样的方式编写C,即在计算数组表达式时计算第一个元素的地址。

引用这个答案的摘录:

就是为什么你不能做的事情

 int a[N], b[N]; a = b; 

因为ab都在该上下文中评估指针 ; 它相当于写3 = 4 。 内存中没有任何内容实际存储数组中第一个元素的地址; 编译器只是在翻译阶段计算它。

也许有必要转过来解决问题,并问为什么你想要分配数组(或结构),而不是使用指针? 这更清晰,更容易理解(至少如果你已经同化了C的Zen),并且它的好处是没有隐瞒许多工作被隐藏在多兆字节数组的“简单”分配之下的事实。