为什么可以直接分配数组?

iBu*_*Bug 5 c arrays variable-assignment function-parameter

请考虑以下代码段:

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);
Run Code Online (Sandbox Code Playgroud)

输出(无编译错误):

2
Run Code Online (Sandbox Code Playgroud)

我无法理解为什么b = a有效.即使数组可能衰减到指针,它们是否应该衰减到const指针(T * const)?

M.M*_*M.M 10

他们不能.

无法分配数组.foo函数中没有数组.int a[]函数参数列表中的语法意味着声明a具有"指向int"的类型.行为与代码完全相同void foo(int *a, int *b).(C11 6.7.6.3/7)

将一个指针指向另一个指针是有效的.结果是两个指针指向同一位置.


即使数组可能衰减到指针,它们不应该衰减为常量指针(T*const)吗?

由数组"衰减"产生的指针是一个右值.所述const限定词仅用于左值(C11 6.7.3/4)有意义.(术语"衰变"是指参数的转换,而不是参数的调整).


Sou*_*osh 8

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

一个参数作为的声明""类型的阵列"",应调整至""合格指针为类型"",其中类型限定符(如果有的话)的那些内的指定[]该阵列类型的推导.[...]

所以,ab实际上是指针,不是数组.

这里没有任何数组类型的赋值,因此代码没有问题.

  • [神圣的废话,你真的可以在括号之间放置类型限定符.](https://ideone.com/J4IoZT)这是一个非常奇怪的语法. (2认同)
  • 令我惊讶的是,人们仍然对18年前添加到该语言中的功能感到惊讶...在计算中,如果不刷新你的知识,那将是很长一段时间! (2认同)

Eri*_*hil 4

是的,对于用以下方式声明的数组参数是有意义的[]为 const 限定指针是有意义的。然而,const这种行为建立时并不存在。

\n\n

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

\n\n

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

\n\n

正如您所观察到的,这使得程序员能够为参数赋值,从而改变了数组的表观基地址。将声明为的参数调整为 是有意义int ArrayA[]int * const ArrayA,这样参数的值ArrayA就无法更改。那么它的行为就更像一个数组,其地址也无法更改,因此这更符合假装传递数组的目标,即使我们正在传递地址。

\n\n

然而,当时const还不存在,所以这是不可能的,const当时没有人想到发明(或者至少做了足够的工作以使其被采用到语言中)。

\n\n

现在世界上有大量的源代码适用于非常量调整。现在改变C语言的规范将会导致现有代码出现问题。

\n