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=150ますb=6。これを説明してください。

a実行すると の値が計算されると思いますがa=4*5*6=120、コンパイラによるとそうではないので、ロジックを説明してください....

4

3 に答える 3

9

ロジックはありません。未定義の動作です。

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

bインターリーブ シーケンス ポイントなしで 3 回変更して読み取ります。

おまけ: 試してみると、別の奇妙な動作が表示されますCUBE(1+2)

于 2013-03-15T17:41:21.987 に答える
5

Luchian Grigore が言ったことに加えて (なぜこの奇妙な動作が観察されるのかを説明しています)、このマクロは恐ろしいものであることに注意してください。 (のように++b) これにより、ステートメントが複数回実行されるためです。

このことから、次の 3 つのことを学ぶ必要があります。

  1. マクロ内でマクロ引数を 2 回以上参照しないでください。この規則には例外もありますが、絶対的なものと考えてください。

  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 * 63つの増分の後にはにaなることがわかります。 150b6

コンパイラが異なれば結果も異なりますが、 cabはこのシーケンスとでの式150についてのみ評価されます。b=35*5*6

于 2013-03-15T18:24:36.873 に答える