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

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

4

3 回答 3

9

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

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

修改和读取b3 次,没有交错序列点。

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

于 2013-03-15T17:41:21.987 回答
5

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

你应该从中学到三件事:

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

  2. 如果可能,尽量避免使用包含副作用的语句调用宏。

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

于 2013-03-15T17:59:47.723 回答
2

在序列中多次更改相同变量的未定义行为。并且因为这个原因,您将使用不同的编译器为您的代码获得不同的结果。

碰巧我a = 150 and b = 6的编译器也得到了相同的结果。

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

您的宏表达式 a = CUBE(++b); 扩展为

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

并且在完整表达b结束之前改变不止一次。.

但是我的编译器如何在低级别转换这个表达式(可能你的编译器做类似的事情,你可以尝试使用相同的技术)。为此,我使用-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变成. 150b6

虽然不同的编译器会产生不同的结果,但我认为, 150cab 只能评估这个序列b=3和你的表达式5*5*6

于 2013-03-15T18:24:36.873 回答