为什么数组可以直接赋值?

Why can arrays be assigned directly?

考虑这个代码片段:

void foo(int a[], int b[]){
    static_assert(sizeof(a) == sizeof(int*));
    static_assert(sizeof(b) == sizeof(int*));
    b = a;
    printf("%d", b[1]);
    assert(a == b); // This also works!
}

int a[3] = {[1] = 2}, b[1];
foo(a, b);

输出(无编译错误):

2

我不明白为什么 b = a 有效。尽管数组可能会衰减为指针,但它们不应该衰减为 const 指针吗 (T * const)?

他们不能。

数组无法分配给。 foo 函数中没有数组。函数参数列表中的语法 int a[] 表示声明 a 的类型为 "pointer to int"。行为与代码 void foo(int *a, int *b) 完全相同。 (C11 6.7.6.3/7)

将一个指针赋给另一个指针是有效的。结果是两个指针都指向同一个位置。


Even though arrays may decay to pointers, shouldn't they decay to const pointers (T * const)?

从数组 "decay" 得到的指针是一个右值。 const 限定符仅对左值有意义 (C11 6.7.3/4)。 ("decay"指的是参数的转换,不是参数的调整)

引用 C11,章节 §6.7.6.3,函数声明符(包括原型)

A declaration of a parameter as ‘‘array of type’’ shall be adjusted to ‘‘qualified pointer to type’’, where the type qualifiers (if any) are those specified within the [ and ] of the array type derivation. [...]

所以,ab其实是指针,不是数组。

这里没有对任何数组类型进行赋值,因此代码没有问题。

是的,将使用 [] 声明的数组参数调整为 const 限定指针是有意义的。但是,const 在建立此行为时并不存在。

在开发 C 语言时,通过传递数组的地址来传递数组是有意义的,或者更具体地说,是第一个元素的地址。您当然不想复制整个数组来传递它。传递地址是使数组为被调用函数所知的一种简单方法。 (我们在 C++ 中看到的引用类型的语义尚未发明。)为了让程序员更容易,以便他们可以编写 foo(ArrayA, ArrayB) 而不是 foo(&Array[0], &ArrayB[0]),将数组转换为的机制发明了指向其第一个元素的指针。 (根据 Dennis M. Ritchie M.M. and The Development of the C Language,这种参数表示法已经存在于 C 的前身语言 B 中。)

很好,您隐藏了转换。但这只是调用函数的地方。在被调用的例程中,正在考虑传递数组的程序员将要编写void foo(int ArrayA[], int ArrayB[])。但由于我们实际上传递的是指针,而不是数组,因此需要将这些更改为 int *ArrayAint *ArrayB。因此创建了声明为数组的参数自动调整为指针的概念。

如您所见,这使程序员能够为参数赋值,从而改变数组的表观基址。将声明为 int ArrayA[] 的参数调整为 int * const ArrayA 是有意义的,这样就无法更改参数 ArrayA 的值。然后它会更像一个数组,其地址也不能更改,所以这更符合即使我们传递地址也假装传递数组的目标。

然而,当时const并不存在,所以这是不可能的,当时也没有人想过发明const(或者至少做了足够的努力得到它采用了语言)。

现在世界上有大量的源代码可以使用非常量调整。现在更改C语言的规范会导致现有代码出现问题。