`int const a[5]`的真正含义是什么?

3
考虑以下数组声明:
int const a[5];

从语言的语义角度来看,它是否与 const int a[5] 完全等价?假设是这样的话,这两个声明本质上都可以理解为 "a 是一个由 5 个常量整数组成的数组"。
另一种阅读第一个声明的方式是 "a 是一个由 5 个整数组成的常量数组"。
显然,这两个语句逻辑上都意味着整个数组是常量;如果一个数组由 5 个常量整数组成,则整个数组都是常量。或者,如果整个数组是常量,则它的所有值也都是常量。
我知道“常量数组”的概念有点无意义,因为数组不是可修改的左值(即它们不能出现在赋值语句的左边)。但是,在哪些情况下这两个声明会产生不同的行为?
Cdecl.org 将第一个声明拒绝为语法错误,而大多数当前编译器则接受它。)
编辑:
链接的重复问题问普通变量的 const 顺序是否重要。对于数组来说,它更加令人困惑,所以我不认为这是一个重复的问题。

const 应用于其左侧的类型,除非它是声明中的第一个,此时它应用于右侧。因此两者是等价的。 - bolov
1
可能是 https://dev59.com/iFvUa4cB1Zd3GeqPxdu_ 的重复问题? 不过,这并没有什么意义。在这里,“5”已经是一个常量了。在使用 const int a[5]; 时,它会在 C++ 中给你一个常量数组。请注意,这在 C 中不起作用(正确)。所以对于 C 来说,它几乎被忽略了。 - Wolph
2
它们是相同的。https://dev59.com/sHVC5IYBdhLWcg3w2lGI - rlbond
2
这里的5已经是一个常量了。-- 不相关。 这并没有真正意义上的含义。-- 错误。 请注意,这在C语言中不起作用(正确)。-- 错误。 所以对于C语言来说,它基本上被忽略了。-- 错误。 你误解了你引用的问题(与此不同),以及Kerrick SB的答案。 - Jim Balter
3
然而,是否存在任何情况下这两个声明会产生不同的行为?关键字的顺序是语法问题--它对语义没有影响。 "Cdecl.org拒绝第一个声明作为语法错误"--那么它是有问题的。 - Jim Balter
2
C语言的声明是由内向外读取的。因此,这意味着a是一个由5个const int组成的数组。改变顺序会使a成为一个由5个int const组成的数组,这是没有意义的,但是C语言允许您将const放在第一位,因为语言设计者混淆了概念,并尽力传播这种混淆。 - Jim Balter
2个回答

12

它与 const int a[5] 完全等价。

是的,它是等价的。

读取第一个声明的另一种方法是"a是由5个int常量组成的数组"。

不完全正确。您所写的声明将const特别应用于数组元素。为了将const应用于数组本身(而不是应用于数组元素),您需要进行以下操作:

int (const a)[5];

但在C语言中,这样的声明是语法上无效的。

可以通过中间typedef间接尝试将const应用于数组本身。

typedef int A[5];
const A a;

但在这种情况下,根据语言规则,const 修饰符会“传递”到数组元素,整个数组就相当于
const int a[5];

请注意,上面的const A a;并不立即等同于const int a[5];。它实际上等同于前面提到的int (const a)[5];(!)。这是一种合法的方法,可以将int (const a)[5];偷偷地传递给编译器。但是int (const a)[5];的生命周期非常短暂 - 它会立即被编译器转换为const int a[5];
如果一个数组由5个常量整数组成,则整个数组都是常量。或者,如果整个数组是常量,则其所有值也是常量。
嗯,这并不完全正确。C语言确实区分数组对象本身和其元素。从概念上讲,它们是不同的实体。例如,正如您自己注意到的那样,语言规范说数组是不可修改的lvalue。当然,这并不能阻止数组元素被修改。
数组作为整体和单个数组元素之间的这种概念上的区别,再加上const的“跌落”行为,正是导致以下不愉快情况的原因。
typedef int A[5];
A a;
const A *p = &a; // ERROR!!!

即它打破了“常规”的const正确性规则,允许我们使用T *值初始化const T *指针。(C++故意更新了const正确性规则以使上述代码表现为“预期”,但C坚持拒绝它。)

1
我把这个东西组合在一起:
#include <stdio.h>

int main()
{
    int const a[5];
    const int b[5];
    int c[5];

    a[0] = 1;
    b[0] = 2;
    c = a;
    c = b;
}

而 gcc (4.1.2) 输出如下:

gcc -o /tmp/x2 /tmp/x2.c
/tmp/x2.c: In function ‘main’:
/tmp/x2.c:9: error: assignment of read-only location
/tmp/x2.c:10: error: assignment of read-only location
/tmp/x2.c:11: error: incompatible types in assignment
/tmp/x2.c:12: error: incompatible types in assignment

根据这个,至少目前看起来没有任何区别。

在提问之前,我进行了相同的实验,但仍然不能排除两者之间可能存在的差异。 - Blagovest Buyukliev

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接