考虑这段代码;
#define A 5
#define B 3
int difference = A - B;
在编译时将“差异”的值硬编码为“2”,还是在运行时计算?
答案 0 :(得分:6)
A
和B
宏有点分散注意力。这样:
#define A 5
#define B 3
int difference = A - B;
完全等同于:
int difference = 5 - 3;
所以我们来讨论后者。
5 - 3
是一个常量表达式,它是一个“可以在翻译期间而不是在运行时进行评估的表达式,因此可以在常量可能出现的任何地方使用”。它也是一个*整型常量表达式。例如,case标签必须是一个整型常量表达式,所以你可以这样写:
switch (foo) {
case 2: /* this is a constant */
...
}
或者这个:
switch (foo) {
case 5 - 3: /* this is a constant expression */
...
}
但请注意,该定义表明可以在翻译期间进行评估,而不是必须。有些上下文需要常量表达式,在这些上下文中,表达式必须在编译时进行评估。
但假设在某个函数中声明difference
,则初始值设定项不是这些上下文之一。
任何值得为它支付的编译器(即使它是免费的)都会在编译时将5 - 3
减少到2
,并生成在{{1}中存储值2
的代码}}。但并不要求这样做。 C标准规定了程序的行为;它没有指定必须如何实现该行为。但可以肯定的是,假设您使用的任何编译器都将difference
替换为5 - 3
。
即使你写:
2
编译器可以合法生成将值int difference = 2;
加载到寄存器中的代码,从中减去5
,并将寄存器的内容存储到3
。这将是一件愚蠢的事情,但语言标准并不排除它。
只要最终结果是difference
的值为difference
,语言标准就不关心它是如何完成的。
另一方面,如果你写:
2
然后编译器必须计算结果,以便它可以诊断错误(你不能有两个具有相同值的案例标签。
最后,如果在文件范围(任何函数之外)定义switch (foo) {
case 5 - 3: /* ... */
case 2: /* ... */
}
,则初始值 必须是常量。但在这种情况下的真正区别不在于是否会在编译时评估difference
,而是你是否允许使用非常量表达式。
参考:2011 C标准的最新草案是N1570(大PDF);常数表达式将在6.6节中讨论。
答案 1 :(得分:5)
标准没有指明这种事情。它没有提到像这样的潜在优化(并且有充分的理由。标准定义了语义,而不是实现)。
为什么不查看编译器的反汇编?这将给你一个明确的答案。
...
让我们这样做。
以下是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
正如您所看到的,它只是将x
的出现替换为静态值2并将其推送到堆栈以调用cout
。它没有在运行时评估表达式。