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

时间:2014-09-18 00:46:39

标签: c++ c

我试着写一个c程序如下?

const int x = 5;

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

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

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

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

4 个答案:

答案 0 :(得分:12)

在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没有采用这个功能。在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范围内的值。

答案 1 :(得分:4)

  

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

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

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

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

答案 2 :(得分:4)

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

  

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

这是正确的,C99和C11(虽然它们在C11中是可选的)。它们无法在声明中初始化,我们可以在6.7.8 初始化部分中看到这一点:

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

  

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

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

  

应具有整数类型,并且只能有操作数   它是整数常量,枚举常量,字符常量,sizeof   结果为整数常量的表达式,以及浮点常量   即时的演员阵容。仅在整数常量表达式中转换运算符   将算术类型转换为整数类型,但作为sizeof的操作数的一部分除外   操作

x不满足。

  

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

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

  

在声明T D中,D的格式为

 D1 [ constant-expressionopt] attribute-specifier-seqopt
     

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

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

答案 3 :(得分:0)

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

试试这个:

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

看看它是否有效。

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