考虑这段代码;
#define A 5
#define B 3
int difference = A - B;
“差异”的值在编译时被硬编码为“2”,还是在运行时计算?
考虑这段代码;
#define A 5
#define B 3
int difference = A - B;
“差异”的值在编译时被硬编码为“2”,还是在运行时计算?
和宏有点让人分心A
。B
这:
#define A 5
#define B 3
int difference = A - B;
完全等同于:
int difference = 5 - 3;
所以让我们讨论后者。
5 - 3
是一个常量表达式,它是一个“可以在翻译期间而不是运行时进行评估,因此可以在常量可能存在的任何地方使用”的表达式。它也是一个 *integer 常量表达式”。例如,case 标签必须是一个整数常量表达式,所以你可以这样写:
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标准规定了程序的行为;它没有指定必须如何实现该行为。但可以肯定的是,您使用的任何编译器都将替换5 - 3
为2
.
即使你写:
int difference = 2;
编译器可以合法地生成代码,将值加载5
到寄存器中,从中减去3
,并将寄存器的内容存储到difference
. 那将是一件愚蠢的事情,但语言标准并不排除它。
只要最终结果difference
具有 value 2
,语言标准就不会关心它是如何完成的。
另一方面,如果你写:
switch (foo) {
case 5 - 3: /* ... */
case 2: /* ... */
}
然后编译器必须计算结果,以便诊断错误(不能有两个具有相同值的 case 标签。
最后,如果您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
如您所见,它只是用x
静态值 2 替换了 的出现,并将其压入堆栈以调用cout
. 它没有在运行时评估表达式。