問:次のC++プログラムの実行結果は?
#include <cstdio>
#define NUMBER 42
static
void func(int& rv)
{
int local = rv;
if (local == NUMBER) {
std::puts("hit");
} else {
std::puts("stay");
}
}
int main()
{
int v; // indeterminate value
func(v);
}
答え:
-
gcc 6.1, -O2オプション:
stay
-
clang 3.9.0, -O2オプション:
hit
は?clangなにやってんの?
clangの場合、マクロNUMBER
を任意の数値にかえたとしても結果はhit
になります。
一方のgccは何となく正しいようにも思えますかね?
コンパイラのバグだろ?
いいえ、C++ソースコードのバグです。悔い改めてください。
変数v
は不定値(indeterminate value)であり、参照rv
を介した変数v
読み取りは未定義動作(undefined behavior)を引き起こします。未定義動作を含むC++プログラムでは、いかなることが起きようともそれはC++言語仕様に準じた振る舞いであり、全ては誤ったC++ソースコードを記述したプログラマの責任です。つらい。
コンパイル結果(出力アセンブリ)を覗く
gcc 6.1(x86-64), -O2オプション:無条件にstay
を出力します。hit
に関する処理は消失しています。
.LC1:
.string "stay"
main:
subq $8, %rsp
movl $.LC1, %edi
call puts
xorl %eax, %eax
addq $8, %rsp
ret
Clang 3.9.0(x86-64), -O2オプション:無条件にhit
を出力します。stay
に関する処理は消失しています。
main:
pushq %rax
movl $.L.str, %edi
callq puts
xorl %eax, %eax
popq %rcx
retq
.L.str:
.asciz "hit"
鼻から悪魔の世界へようこそ。
関連POST
一連の「null安全」話にインスパイアされた記事ですが、正直言って直接的には関係しません。C/C++言語系の未定義動作は怖いという実例として。