2013-03-15 142 views
4
#include<stdio.h> 
#define CUBE(x) (x*x*x) 

int main() 
{ 
int a, b=3; 
a = CUBE(++b); 
printf("%d, %d\n", a, b); 
return 0; 
} 

该代码返回值a=150b=6。请解释一下。解释这个C程序的输出?

我认为,当它执行的a值将被计算为a=4*5*6=120但根据编译器是不正确的,所以请解释逻辑....

+0

体现在哪里?如果我使用gcc 4.2.1,我得到结果:a = 120,b = 6 – 2013-03-15 17:50:17

+1

@Magnus:任何结果都是可能的。你可以得到'a = 120,b = 6'或者你可以得到'土豆'。这是未定义的行为。 – 2013-03-15 18:02:01

回答

9

有没有逻辑,这是不确定的行为,因为

++b * ++b * ++b; 

修改并读取3次,没有交错序列点。

奖励:如果您尝试CUBE(1+2),您会看到另一个奇怪的行为。

5

除了什么Luchian格里戈里说(这也解释了为什么你看到这个怪异的行为),你应该注意到这个宏可怕:它可能会导致微妙,很难追踪下的错误,尤其是在与一个叫语句有副作用(如++b),因为这会导致语句执行多次。

您应该从这次发现三条东西:

  1. 决不参考宏参数比宏一次。虽然这条规则有例外,但您应该将其视为绝对的。

  2. 尝试避免在可能的情况下调用带有包含副作用的语句的宏。

  3. 尝试到避免函数式的宏如果可能的话。改用内联函数。

2

它的undefined behavior在一个序列中多次改变相同的变量。而且因为这个原因,你将得到不同的编译器为你的代码的不同结果。

通过机会我也得到了同样的结果a = 150 and b = 6与我的编译器。

gcc version 4.6.3 (Ubuntu/Linaro 4.6.3-1ubuntu5) 

您的宏表达a = CUBE(++b);大片如

a = ++b * ++b * ++b; 

而且bfull expression.年底前改变更多然后一次。但我的编译器如何在低级别转换此表达式(可能是您的编译器做类似的操作,您可以尝试使用相同的技术)。为此,我使用-S选项编译了源代码C,并获得了汇编代码。

gcc x.c -S 

您将获得x.s文件。

我显示部分有用的汇编代码(阅读评论

因为你想知道如何做150输出,这就是为什么我加入我的答案

movq %rsp, %rbp 
.cfi_def_cfa_register 6 
subq $16, %rsp 
movl $3, -8(%rbp) // b = 3 

addl $1, -8(%rbp) // b = 4 
addl $1, -8(%rbp) // b = 5 

movl -8(%rbp), %eax // eax = b 
imull -8(%rbp), %eax // 5*5 = 25 

addl $1, -8(%rbp) // 6 `b` become 6 and 

imull -8(%rbp), %eax // 6 * 25 = 150 
movl %eax, -4(%rbp) // 150 assign to `a` become 150 

movl $.LC0, %eax  // printf function stuff... 
movl -8(%rbp), %edx 
movl -4(%rbp), %ecx 
movl %ecx, %esi 
movq %rax, %rdi 

在检查这汇编代码我可以理解它评估表达式,如 a = 5 * 5 * 6因此a变成150,并在三个增量后b变为6

虽然不同的编译器产生不同的结果,但我认为,150驾驶室只评估了此序列b=3和您使用什么编译您在5*5*6