考虑一下这段代码;
#define A 5
#define B 3
int difference = A - B;
"difference"的值在编译时是硬编码为"2",还是在运行时计算?
A
和B
宏有点分散注意力。此:
#define A 5
#define B 3
int difference = A - B;
与此完全等效:
int difference = 5 - 3;
那么让我们来讨论后者。
5 - 3
是一个常量表达式,它是一个"可以在翻译过程中而不是运行时进行评估,因此可以在常量所在的任何地方使用"的表达式。它也是一个*integer常量表达式
switch (foo) {
case 2: /* this is a constant */
...
}
或者这个:
switch (foo) {
case 5 - 3: /* this is a constant expression */
...
}
但请注意,定义中说它可以在翻译过程中被求值,而不是必须要求值。有些上下文需要常量表达式,在这些上下文中,表达式必须在编译时求值。
但是,假设difference
是在某个函数内部声明的,初始化器不是这些上下文之一。
任何一个值得你花钱的编译器(即使它是免费的)都会在编译时将5 - 3
减少到2
,并生成将值2
存储在difference
中的代码。但这并不是必须的。C标准规定了程序的行为;它没有指定必须如何实现该行为。但可以放心地假设,无论使用什么编译器,都会用2
替换5 - 3
。
即使你写:
int difference = 2;
编译器可以合法地生成代码,将值5
加载到寄存器中,从中减去3
,并将寄存器的内容存储到difference
中。这将是一件愚蠢的事情,但语言标准并不排除它
只要最终结果是difference
具有值2
,语言标准就不在乎它是如何实现的。
另一方面,如果你写:
switch (foo) {
case 5 - 3: /* ... */
case 2: /* ... */
}
则编译器必须计算结果,以便诊断错误(不能有两个具有相同值的大小写标签。
最后,如果在文件范围(任何函数之外)定义difference
,则初始值必须是常量。但在这种情况下,真正的区别不是5 - 3
是否会在编译时求值,而是是否允许使用非常量表达式。
参考资料:2011年C标准的最新草案为N1570(大PDF);常数表达式在第6.6节中进行了讨论。
标准没有指定这类内容。它没有提到像这样的潜在优化(这是有充分理由的。标准定义的是语义,而不是实现)。
为什么不看看编译器的反汇编呢?这将给你一个明确的答案。
所以让我们这样做吧。
以下是VC++10:的输出
#include <iostream>
#define A 5
#define B 3
int main() {
int x = A - B;
std::cout << x; // make sure the compiler doesn't toss it away
010A1000 mov ecx,dword ptr [__imp_std::cout (10A2048h)]
010A1006 push 2
010A1008 call dword ptr [__imp_std::basic_ostream<char,std::char_traits<char> >::operator<< (10A2044h)]
return 0;
010A100E xor eax,eax
正如您所看到的,它只是用静态值2替换了x
的出现,并将其推送到堆栈中,以便调用cout
。它没有在运行时计算表达式。