为什么 const int x = 5;不是 C 中的常量表达式?
Why is const int x = 5; not a constant expression in C?
本以为C对我来说没有更多的惊喜,但这让我很意外。
const int NUM_FOO = 5;
....
int foo[NUM_FOO];
==>error C2057: expected constant expression
我的 C++ 经验使我在内部尽可能地弃用 #define
。所以这真的是一个惊喜。 VS2019,用/TC编译。我以为 C99 无论如何都允许可变大小数组。
谁能解释为什么会发生拒绝,因为编译器在编译时肯定知道数组的大小?
C99不是允许变长数组吗?
C 中的 const
没有声明编译时常量。如果你想避免使用 #define
并且想要一个可以出现在调试器中的符号名称,你可以使用 enum
常量。
C99 支持 VLA。但是,
带有 const
限定符的变量不符合常量表达式的条件。
关于常量表达式状态的 C11 standard 第 6.6p6 节
An integer constant expression shall have integer type and
shall only have operands that are integer constants,
enumeration constants, character constants, sizeof
expressions
whose results are integer constants, _Alignof
expressions, and
floating constants that are the immediate operands of casts. Cast
operators in an integer constant expression shall only convert
arithmetic types to integer types, except as part of an
operand to the sizeof
or _Alignof
operator
请注意,不包括 const
个符合条件的整数对象。
这意味着int foo[NUM_FOO];
是一个变长数组,从6.7.6.2p4部分定义如下:
If the size is not present, the array type is an incomplete type. If
the size is *
instead of being an expression, the array type
is a variable length array type of unspecified size, which can
only be used in declarations or type names with function prototype
scope; such arrays are nonetheless complete types. If the size is
an integer constant expression and the element type has a known
constant size, the array type is not a variable length array
type; otherwise, the array type is a variable length array
type.
至于你得到的错误,那是因为Visual Studio不完全符合C99并且不支持可变长度数组。
在 C 中,这个声明:
const int NUM_FOO = 5;
不会使 NUM_FOO
成为常量表达式。
要记住(是的,这有点违反直觉)是 const
并不意味着 不变 。 常量表达式 大致是可以在编译时求值的表达式(如 2+2
或 42
)。 const
类型限定符,尽管其名称显然源自英文单词 "constant",但实际上意味着 "read-only".
例如,考虑一下这些是完全有效的声明:
const int r = rand();
const time_t now = time(NULL);
const
只是表示 r
或 now
的值在初始化后不能修改。这些值显然要到执行时间才能确定。
(C++ 有不同的规则。它确实使 NUM_FOO
成为常量表达式,后来的语言版本为此添加了 constexpr
。C++ 不是 C。)
至于可变长度数组,是的,C 在 C99 中添加了它们(并在 C11 中使它们可选)。但正如 指出的那样,VS2019 不支持 C99 或 C11。
(C++ 不支持 VLA。这:const int NUM_FOO = 5; int foo[NUM_FOO];
在 C99 和 C++ 中都是合法的,但出于不同的原因。)
如果要定义 int
类型的命名常量,可以使用 enum
:
enum { NUM_FOO = 5 };
或老式宏(不限于类型 int
):
#define NUM_FOO 5
和 都是正确的。我只是添加了更多上下文。
除了现有的一切都很好的答案之外,const
限定对象从根本上说一般不能成为常量表达式的原因,在 "one that can be evaluated at compile time" 的意义上(如前所述在 Keith 的回答中),是它可以有 external linkage。例如,您可以在 foo.c
const int NUM_FOO = 5;
在bar.c
中:
extern int NUM_FOO;
...
int foo[NUM_FOO];
本例中NUM_FOO
的值在编译时无法得知bar.c
;直到你选择 link foo.o
和 bar.o
.
才知道
C 的 "constant expression" 模型与允许翻译单元(源文件)独立翻译(编译)为不需要进一步高级转换的形式的属性密切相关 link .这也是为什么不能在常量表达式中使用地址的原因,地址常量表达式除外,地址常量表达式本质上仅限于对象的地址加上常量。
本以为C对我来说没有更多的惊喜,但这让我很意外。
const int NUM_FOO = 5;
....
int foo[NUM_FOO];
==>error C2057: expected constant expression
我的 C++ 经验使我在内部尽可能地弃用 #define
。所以这真的是一个惊喜。 VS2019,用/TC编译。我以为 C99 无论如何都允许可变大小数组。
谁能解释为什么会发生拒绝,因为编译器在编译时肯定知道数组的大小?
C99不是允许变长数组吗?
-
C 中的
const
没有声明编译时常量。如果你想避免使用#define
并且想要一个可以出现在调试器中的符号名称,你可以使用enum
常量。C99 支持 VLA。但是,
带有 const
限定符的变量不符合常量表达式的条件。
关于常量表达式状态的 C11 standard 第 6.6p6 节
An integer constant expression shall have integer type and shall only have operands that are integer constants,
enumeration constants, character constants,sizeof
expressions whose results are integer constants,_Alignof
expressions, and floating constants that are the immediate operands of casts. Cast operators in an integer constant expression shall only convert arithmetic types to integer types, except as part of an operand to thesizeof
or_Alignof
operator
请注意,不包括 const
个符合条件的整数对象。
这意味着int foo[NUM_FOO];
是一个变长数组,从6.7.6.2p4部分定义如下:
If the size is not present, the array type is an incomplete type. If the size is
*
instead of being an expression, the array type is a variable length array type of unspecified size, which can only be used in declarations or type names with function prototype scope; such arrays are nonetheless complete types. If the size is an integer constant expression and the element type has a known constant size, the array type is not a variable length array type; otherwise, the array type is a variable length array type.
至于你得到的错误,那是因为Visual Studio不完全符合C99并且不支持可变长度数组。
在 C 中,这个声明:
const int NUM_FOO = 5;
不会使 NUM_FOO
成为常量表达式。
要记住(是的,这有点违反直觉)是 const
并不意味着 不变 。 常量表达式 大致是可以在编译时求值的表达式(如 2+2
或 42
)。 const
类型限定符,尽管其名称显然源自英文单词 "constant",但实际上意味着 "read-only".
例如,考虑一下这些是完全有效的声明:
const int r = rand();
const time_t now = time(NULL);
const
只是表示 r
或 now
的值在初始化后不能修改。这些值显然要到执行时间才能确定。
(C++ 有不同的规则。它确实使 NUM_FOO
成为常量表达式,后来的语言版本为此添加了 constexpr
。C++ 不是 C。)
至于可变长度数组,是的,C 在 C99 中添加了它们(并在 C11 中使它们可选)。但正如
(C++ 不支持 VLA。这:const int NUM_FOO = 5; int foo[NUM_FOO];
在 C99 和 C++ 中都是合法的,但出于不同的原因。)
如果要定义 int
类型的命名常量,可以使用 enum
:
enum { NUM_FOO = 5 };
或老式宏(不限于类型 int
):
#define NUM_FOO 5
除了现有的一切都很好的答案之外,const
限定对象从根本上说一般不能成为常量表达式的原因,在 "one that can be evaluated at compile time" 的意义上(如前所述在 Keith 的回答中),是它可以有 external linkage。例如,您可以在 foo.c
const int NUM_FOO = 5;
在bar.c
中:
extern int NUM_FOO;
...
int foo[NUM_FOO];
本例中NUM_FOO
的值在编译时无法得知bar.c
;直到你选择 link foo.o
和 bar.o
.
C 的 "constant expression" 模型与允许翻译单元(源文件)独立翻译(编译)为不需要进一步高级转换的形式的属性密切相关 link .这也是为什么不能在常量表达式中使用地址的原因,地址常量表达式除外,地址常量表达式本质上仅限于对象的地址加上常量。