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

时间:2015-01-10 21:34:05

标签: c arrays variable-assignment

我试图谷歌这个并阅读:

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

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

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

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

6 个答案:

答案 0 :(得分:20)

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

阵列是另一回事。大多数数组对象的大小在运行时之前都不会确定。一个很好的例子是argv。运行时环境为每个命令行参数构造一个char数组,并为包含参数指针的char*数组构造一个数组。这些内容通过main argvchar**以及char[]元素指向的动态分配的argv数组提供给int[10]

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

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

如果int[20]和{{1}}是不具有隐式转换的不同类型,则数组操作不灵活(例如,参见Pascal)。

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

答案 1 :(得分:9)

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

此语言不允许分配数组。如果您想复制相同大小的数组,memcpy可以满足您的需求。写memcpy(a, b, sizeof a)而不是a = b当然不是一个很大的复杂因素。它具有可以推广到不同大小的数组和数组切片的额外优势。

有趣的是,您在K& R C中提到的struct分配变通方法无法正常工作。您必须逐个分配成员,或者再次使用{{1} }。 K& R的 C编程语言第一版提到memcpy作为未来语言实现的功能。这最终发生在C89上。

答案 2 :(得分:9)

答案很简单:在委员会介入之前从未被允许(即使struct - 任务被认为太重了),考虑到阵列衰减,允许它有各种各样的有趣的后果。

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

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?

答案 3 :(得分:8)

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

阅读this paper(特别是标题为" Embryonic C"部分)的某些背景;我还有一个更详细的答案here

<小时/> 1。除了Perl或PHP 2 之外,大多数公然的语言WTF通常是设计意外或妥协的结果;大多数语言都没有故意设计为愚蠢的
我只是拖了一下; Perl和PHP是直接的混乱。

答案 4 :(得分:5)

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

引用this answer的摘录:

  

这就是为什么你不能做像

这样的事情
int a[N], b[N];
a = b;
     

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

答案 5 :(得分:2)

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