为什么在C中不允许将数组的大小作为常量变量但在C ++中允许?

我试着写ac程序如下?

const int x = 5; int main() { int arr[x] = {1, 2, 3, 4, 5}; } 

当我尝试使用gcc进行编译时,这会发出警告,如下所示。

simple.c:9:错误:可能无法初始化可变大小的对象。

但是在C ++中允许这样做。 当我将x作为数组大小传递时,为什么x不被视为常量?

在C const中,并不意味着“常量”(即,在编译时可评估)。 它只是意味着只读

例如,在一个函数中,这个:

 const int r = rand(); const time_t now = time(NULL); 

完全有效。

定义为const int的对象的名称不是常量表达式 。 这意味着(在C​​99之前的C中,在所有版本的C ++中)它不能用于定义数组的长度。

尽管C99(以及可选的C11)支持可变长度数组 (VLA),但它们无法初始化。 原则上,编译器在定义VLA时不知道VLA的大小,因此无法检查初始化程序是否有效。 在您的特定情况下,编译器很可能能够解决它,但语言规则旨在涵盖更一般的情况。

C ++ 几乎相同,但C ++有一个C缺乏的特殊规则:如果一个对象被定义为const ,并且它的初始化是一个常量表达式,那么对象的名称本身就是一个常量表达式(至少对于整数类型) 。

C没有采用这个function的确没有充分的理由。 在C中,如果你想要一个整数类型的名称常量,通常的方法是使用一个宏:

  #define LEN 5 ... int arr[LEN] = {1, 2, 3, 4, 5}; 

请注意,如果更改LEN的值,则必须重新编写初始值设定项。

另一种方法是使用匿名enum

  enum { LEN = 5 }; ... int arr[LEN] = {1, 2, 3, 4, 5}; 

枚举常量的名称实际上是常量表达式。 在C中,由于历史原因,它始终是int类型; 在C ++中,它是枚举类型。 不幸的是,这个技巧仅适用于int类型的常量,因此它仅限于INT_MININT_MAX范围内的值。

当我将x作为数组大小传递时,为什么x不被视为常量?

因为在C中,常量表达式不能涉及任何变量的值,即使是const变量。 (这就是为什么C如此依赖于宏常量的原因之一,而C ++会将const变量用于同一目的。)

另一方面,在C ++中,如果x被声明为const int x = 5;x肯定是一个常量表达式const int x = 5;

如果你的问题是为什么 C ++在使用常量表达式时比C语言更自由,我认为这是支持元编程,并允许使用模板在编译时执行复杂的计算。

我想几乎每个人都误解了错误,错误说:

可能无法初始化可变大小的对象。

这是正确的,C99和C11( 尽管它们在C11中是可选的 )。 它们不能在声明中初始化,我们可以从第6.7.8初始化中看到:

它被视为VLA,因为与C ++不同,C期望一个整数constnt表达式

如果size是一个整型常量表达式,并且元素类型具有已知的常量大小,则数组类型不是可变长度数组类型;

并且整数常量表达式具有以下限制:

应具有整数类型,并且只能具有整数常量的操作数,枚举常量,字符常量,结果为整数常量的sizeof表达式,以及作为强制转换的直接操作数的浮点常量。 整数常量表达式中的转换运算符只能将算术类型转换为整数类型,除非作为sizeof运算符的操作数的一部分。

哪个x不满足。

要初始化的实体的类型应该是未知大小的数组或不是可变长度数组类型的对象类型。

在C ++中,这不是一个可变长度数组,因为x被认为是一个常量表达式 ,我们可以从草案C ++标准部分8.3.4 Arrays下的第8声明 8.3.4说明:

在TD的声明中,D表格

  D1 [ constant-expressionopt] attribute-specifier-seqopt 

[…]如果存在constant-expression(5.19),它应该是std :: size_t类型的转换常量表达式,并且其值应大于零。 常量表达式指定数组中的(元素数)的边界。 如果常量表达式的值为N,则该数组具有编号为0到N-1的N个元素[…]

如果我们从x的声明中删除了const ,它会因为两个原因之一而失败,或者编译器支持VLA作为扩展,并且它会因为在C中失败或者编译器不支持VLA作为扩展而失败。因此,声明无效。

我假设您正在使用C99编译器(它支持动态大小的数组)。 发生的事情是编译器在编译时无法确定数组在内存方面的表现。

试试这个:

 int arr[x]; memset( arr, 0, x*sizeof(int) ); 

并看看它是否有效。

我认为可能导致这种情况的另一件事是const并不是真正意义上的任何东西,因此编译器可能不会让你做你想做的事情。 您可以看到,有几种方法可以更改const变量,这也是c#为什么不提供const关键字的部分原因。 const更像是对人类的警报而不是其他任何东西。