常量之间的算术运算

时间:2012-03-09 01:25:09

标签: c c-preprocessor compile-time-constant

考虑这段代码;

#define A 5
#define B 3

int difference = A - B;

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

2 个答案:

答案 0 :(得分:6)

AB宏有点分散注意力。这样:

#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。它没有在运行时评估表达式。