#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
、コンパイラによるとそうではないので、ロジックを説明してください....
#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
、コンパイラによるとそうではないので、ロジックを説明してください....
ロジックはありません。未定義の動作です。
++b * ++b * ++b;
b
インターリーブ シーケンス ポイントなしで 3 回変更して読み取ります。
おまけ: 試してみると、別の奇妙な動作が表示されますCUBE(1+2)
。
Luchian Grigore が言ったことに加えて (なぜこの奇妙な動作が観察されるのかを説明しています)、このマクロは恐ろしいものであることに注意してください。 (のように++b
) これにより、ステートメントが複数回実行されるためです。
このことから、次の 3 つのことを学ぶ必要があります。
マクロ内でマクロ引数を 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
3つの増分の後にはにa
なることがわかります。 150
b
6
コンパイラが異なれば結果も異なりますが、 cabはこのシーケンスとでの式150
についてのみ評価されます。b=3
5*5*6