6

考虑这段代码;

#define A 5
#define B 3

int difference = A - B;

“差异”的值在编译时被硬编码为“2”,还是在运行时计算?

4

2 回答 2

6

和宏有点让人分心AB这:

#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 - 32并生成将值存储2difference. 但这不是必须的。C标准规定了程序的行为;它没有指定必须如何实现该行为。但可以肯定的是,您使用的任何编译器都将替换5 - 32.

即使你写:

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 节中讨论。

于 2012-03-09T02:05:43.440 回答
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. 它没有在运行时评估表达式。

于 2012-03-09T01:27:53.447 回答