解释这个C程序的输出?

时间:2013-03-15 17:39:26

标签: c

#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的值。请解释一下。

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

3 个答案:

答案 0 :(得分:9)

没有逻辑,它是未定义的行为,因为

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

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

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

答案 1 :(得分:5)

除了Luchian Grigore所说的(这解释了为什么你会观察到这种奇怪的行为),你应该注意到这个宏可怕:它会导致微妙且非常难以追踪的bug,特别是当使用具有副作用的语句(如++b)调用时,因为这会导致语句多次执行。

你应该从中学到三件事:

  1. 从不在宏中多次引用宏参数。虽然这条规则有例外,但您应该将其视为绝对的。

  2. 尝试避免使用包含副作用的语句调用宏。

  3. 尽可能避免类似函数的宏。请改用内联函数。

答案 2 :(得分: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个文件。

我正在展示部分有用的asm代码(读取评论

因为您想知道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的{​​{1}}以及5*5*6

中的表达式进行评估