C 为什么int x[n]错误,其中n是常量值?

C 为什么int x[n]错误,其中n是常量值?,c,arrays,constants,c99,c++,C,Arrays,Constants,C99,C++,我无法理解为什么这样做是错误的: const int n = 5; int x[n] = { 1,1,3,4,5 }; 即使n已经是常量值 对于GNU编译器来说,这样做似乎是正确的: const int n = 5; int x[n]; /*without initialization*/ 我知道C99的VLA特性,我认为这与正在发生的事情有关,但是 我只需要澄清一下背景中发生了什么 为什么int x[n]错误,其中n是const值 n不是常数const仅承诺n是一个“只读”变量,在程序执

我无法理解为什么这样做是错误的:

const int n = 5; 
int x[n] = { 1,1,3,4,5 };
即使
n
已经是常量值

对于GNU编译器来说,这样做似乎是正确的:

const int n = 5;
int x[n]; /*without initialization*/
我知道C99的VLA特性,我认为这与正在发生的事情有关,但是 我只需要澄清一下背景中发生了什么

为什么
int x[n]
错误,其中
n
const

n
不是常数
const
仅承诺
n
是一个“只读”变量,在程序执行期间不应修改。
请注意,与中不同,
const
限定变量不是常量。因此,声明的数组是可变长度数组。
不能使用初始值设定项列表初始化可变长度数组

C11-§6.7.9/3:

要初始化的实体类型应为大小未知的数组或完整的对象类型,而不是可变长度数组类型

您可以使用
#define
enum
n
设为常量

#define n 5
int x[n] = { 1,1,3,4,5 };   

即使
n
是一个
const
,也不能使用它来定义数组的大小,除非您希望创建VLA。但是,不能使用初始值设定项列表来初始化VLA

使用宏创建固定大小的数组

#define ARRAY_SIZE 5

int x[ARRAY_SIZE] = { 1,1,3,4,5 };

如果要彻底初始化数组,则让编译器推断数组大小更容易、更安全、更易于维护:

int x[] = { 1,1,3,4,5 };
const int n = sizeof(x) / sizeof(*x) ; 

然后,要更改数组大小,只需更改初始值设定项的数量,而不必更改大小和初始值设定项列表以匹配。当有许多初始值设定项时特别有用。

要记住的关键是
const
和“constant”意味着两个完全不同的东西


const
关键字实际上意味着“只读”。常量是数字文字,例如
42
1.5
(或枚举或字符常量)。常量表达式是一种特殊的表达式,可以在编译时进行计算,例如
2+2

因此,在声明中:

const int n = 5;
表达式
n
引用对象的值,它不被视为常量表达式。一个典型的编译器将优化对
n
的引用,用它将用于文本
5
的相同代码替换它,但这不是必需的——表达式是否为常量的规则由语言决定,而不是由当前编译器的聪明度决定

常量(只读)和常量(编译时计算)之间的差异示例如下:

const
关键字意味着在初始化后不允许修改
now
的值,但是
time(NULL)
的值在运行时之前显然无法计算

因此:

const int n = 5;
int x[n];
与没有
const
关键字相比,它在C中的有效性不高

语言可以(而且我可能应该)将
n
评估为一个常量表达式;只是没有这样定义。(C++确实有这样的规则;参见C++标准或对血腥细节的适当引用)。 如果需要值为
5
的命名常量,最常用的方法是定义宏:

#define N 5
int x[N];
另一种方法是定义枚举常量:

enum { n = 5 };
int x[n];
枚举常量是常量表达式,类型始终为
int
(这意味着此方法不适用于
int
以外的类型)。这可以说是对
enum
机制的滥用

从1999年标准开始,可以使用非常量大小定义数组;这是一个VLA或可变长度数组。这样的数组只允许在块范围内使用,并且可能没有初始值设定项(因为编译器无法检查初始值设定项的元素数是否正确)

但考虑到您的原始代码:

const int n = 5; 
int x[n] = { 1,1,3,4,5 };
您可以让编译器从初始值设定项推断长度:

int x[] = { 1,1,3,4,5 };
然后可以根据数组的大小计算长度:

const int x_len = sizeof x / sizeof x[0];

您的代码在语义上是否与此处的
myfunc()
不同:

void myfunc(const int n) { 
    int x[n] = { 1,1,3,4,5 };
    printf("%d\n", x[n-1]);
    *( (int *) &n) = 17;    //  Somewhat less "constant" than hoped...
    return ;
}

int main(){
    myfunc(4);
    myfunc(5);
    myfunc(6);  //  Haven't actually tested this.  Boom?  Maybe just printf(noise)?
    return 0;
}
n
真的都是常数吗?您认为编译器应该为
x[]
分配多少空间(因为这是编译器的工作)


正如其他人所指出的,cv限定符
const
并不意味着“编译期间以及编译之后的所有时间都是常量的值”。它的意思是“本地代码不应该更改的值(尽管可以更改)。

@Yahia Farghaly
n
是一个变量,它是常量,但不是常量
const
在C中实际上意味着只读。@chux:实际上,它不保证是只读的。它只是程序员对编译器的一种保证。编译器不能检测到所有的违反,也不需要运行时环境.@ OALF - true,它是一个错误的过度简化的OP.注意:在C++中, const > -合格的变量可能是或可能不是常数表达式,例如<代码> const int n=rand()% 5;int x[n] C正确,但C++不正确!artm;您可以使用
#define
enum
。我怀疑如果您在
n
的定义上添加
static
,它会起作用<代码>静态const 将是真正的全局编译时间常数,而不是堆栈存储常数。@ SimoWranger-:C不是C++。
const
的语义是区别之一。C没有符号常量。例如,
1
是C标准中的整数常量<代码>常量int i仍然是一个变量
const
只是向编译器承诺不会直接或间接更改变量。对于第一种情况,大多数编译器将发出警告,对于后一种情况(例如,通过指针),它们在检测所有po方面非常有限
void myfunc(const int n) { 
    int x[n] = { 1,1,3,4,5 };
    printf("%d\n", x[n-1]);
    *( (int *) &n) = 17;    //  Somewhat less "constant" than hoped...
    return ;
}

int main(){
    myfunc(4);
    myfunc(5);
    myfunc(6);  //  Haven't actually tested this.  Boom?  Maybe just printf(noise)?
    return 0;
}