c-常数之间的算术运算



考虑一下这段代码;

#define A 5
#define B 3
int difference = A - B;

"difference"的值在编译时是硬编码为"2",还是在运行时计算?

AB宏有点分散注意力。此:

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

最新更新