为什么在 C 中不允许分配给数组?

Why is assigning to an array not allowed in C?

如果我写:

int some_arr[4];
some_arr = {0, 1, 2, 3};

然后我的编译器(在本例中为 GCC)会抱怨我在 { 之前没有表达式。所以我需要使用复合文字,很好:

int some_arr[4];
some_arr = (int[]){0, 1, 2, 3};

现在我们看到我不能为数组赋值。

什么?

我可以 "circumvent" 使用 memcpy(some_arr, (int[]){0, 1, 2, 3}, sizeof(int[4])) 之类的东西,或者通过一个一个地(或通过循环)分配给 some_arr 的每个元素。我不能想象一下 GCC 无法解析我所写内容中的各个赋值(一个不关心用户的惰性编译器甚至可能在预处理器中进行解析),所以它似乎归结为 "the standard said no." 那么为什么 标准说这个特定的东西是禁区?

我不是在寻找标准中说不允许的语言,而是在寻找标准的这一部分是如何形成的历史教训。

来自 ISO/IEC 9899:1999 on assignment operator constrains

§6.5.16 An assignment operator shall have a modifiable lvalue as its left operand.

然后是可修改的左值

§6.3.2.1 A modifiable lvalue is an lvalue that does not have array type, does not have an incomplete type, does not have a const-qualified type, and if it is a structure or union, does not have any member (including, recursively, any member or element of all contained aggregates or unions) with a const-qualified type.

为什么不呢?可能是因为数组名称最有可能衰减为指向第一个元素的指针。


但是,允许使用结构包裹的数组赋值,例如:

//gcc 5.4.0

#include  <stdio.h>

struct A
{
    int arr[3];
    int b;
};

struct A foo()
{
    struct A a = {{1, 2, 3},10};
    return a;
}

int main(void)
{
    struct A b = foo();
    for (int i=0; i<3; i++)
          printf("%d\n",b.arr[i]);
    printf("%d\n", b.b);
}

产量

1
2
3
10

tl;博士:

因为 C 数组会退化为指针,并且没有为程序员提供避免它的方法。

长答案:

写的时候

int arr[4];

从那一刻起,每次在动态上下文中使用 arr 时,C 都会将 arr 视为 &arr[0],即数组衰减为指针(请参阅还有 here and ).

因此:

arr = (int[]){0, 1, 2, 3};

被认为是

&arr[0] = (int[]){0, 1, 2, 3};

无法分配。编译器 可以 使用 memcpy() 实现完整的数组复制,但是 C 必须提供一种方法来告诉编译器何时衰减到指针,何时不衰减。

请注意,动态上下文不同于静态上下文。 sizeof(arr)&arr是编译时处理的静态上下文,其中arr被当作数组处理。

同样,初始化

int arr[4] = {0, 1, 2, 3};

int arr[] = {0, 1, 2, 3};

是静态上下文 - 这些初始化发生在程序加载到内存中时,甚至在它执行之前。

是:

Except when it is the operand of the sizeof operator or the unary & operator, or is a string literal used to initialize an array, an expression that has type ‘‘array of type’’ is converted to an expression with type ‘‘pointer to type’’ that points to the initial element of the array object and is not an lvalue. If the array object has register storage class, the behavior is undefined.

当数组位于结构内部时,例如

struct s {
    int arr[4];
};
struct s s1, s2;

然后再次使用s1.arr就像&s1.arr[0]一样,无法赋值。

但是,虽然 s1 = s2 是动态上下文,但并未引用数组。编译器知道它需要复制整个数组,因为它是结构定义的一部分,并且此赋值是隐式生成的。例如,如果编译器选择使用 memcpy() 实现结构赋值,则自动复制数组。