我试着写一个c程序如下?
const int x = 5;
int main()
{
int arr[x] = {1, 2, 3, 4, 5};
}
当我尝试使用gcc进行编译时,这会发出警告。
simple.c:9:错误:可能无法初始化变量大小的对象。
但是C ++允许这样做。当我将x作为数组大小传递时,为什么x不被视为常量?
答案 0 :(得分:12)
在C const
中并不意味着“常数”(即,在编译时可评估)。它仅仅意味着只读。
例如,在函数中,这个:
const int r = rand();
const time_t now = time(NULL);
完全有效。
定义为const int
的对象的名称不是常量表达式。这意味着(在C99之前的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_MIN
到INT_MAX
范围内的值。
答案 1 :(得分:4)
当我将x作为数组大小传递时,为什么x不被视为常量?
因为在C中,常量表达式不能涉及任何变量的值,即使是const
个变量。 (这是C依赖于宏常量的一个原因,而C ++会将const
变量用于同一目的。)
另一方面,在C ++中,如果将x
声明为x
,const 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更像是对人类的警报而不是其他任何东西。