Upgrade to Pro — share decks privately, control downloads, hide ads and more …

未定義動作でFizz Buzz / Undefined Fizz Buzz

kaityo256
December 06, 2023

未定義動作でFizz Buzz / Undefined Fizz Buzz

kaityo256

December 06, 2023
Tweet

More Decks by kaityo256

Other Decks in Programming

Transcript

  1. 7 19 Fizz Buzz Fizz Buzzが組める能力、 実運用で必要か? Fizz Buzzも組めない プログラマがいる!

    定期的に話題になる 話題になる度に変態超絶解法も話題に
  2. 10 19 未定義動作でFizz Buzz #include <cstdio> int main(){ int a

    = 0, b = 0; a = --a + ++a + ++a; b = ++b + ++b + a; for (int i=1;i<16;i++){ if (i%b==0){ printf("%s¥n",a?"buzz":"fizz"); }else{ printf("%d¥n",i); } } } こんなコードを書いてみた
  3. 11 19 未定義動作でFizz Buzz $ clang++ fizz.cpp; ./a.out 1 2

    fizz 4 5 fizz 7 8 fizz 10 11 fizz 13 14 fizz $ g++ fizz.cpp; ./a.out 1 2 3 4 buzz 6 7 8 9 buzz 11 12 13 14 buzz clang++でコンパイルすると 3の倍数の時だけfizzがでる g++でコンパイルすると 5の倍数の時だけbuzzがでる ※ Fizz Buzzの処理?シェルでなんとかすればいいんじゃん?
  4. 12 19 なにが起きたか? int b = 0; b = ++b

    + ++b + a; int a = 0; a = --a + ++a + ++a; clang++ではa=0に、g++ではa=1になる clang++ではb=3に、g++ではb=5になる GCCかどうかのフラグとして使える 整数剰余に使える
  5. 13 19 なにが起きたか? int a = 1; int b =

    ++a + ++a; 複数の前置インクリメントを式に入れたら動作は未定義 int a = 1; int tmp1 = a + 1; a = tmp1; int tmp2 = a + 1; a = tmp2; int b = tmp1 + tmp2; int a = 1; a = a + 1; a = a + 1; int tmp = a + a int b = tmp; clang++の解釈 g++の解釈
  6. 14 19 clang++の気持ち int func(int a){ return ++a; } $

    clang -emit-llvm -S test.c ; Function Attrs: noinline nounwind optnone uwtable define dso_local i32 @func(i32 %0) #0 { %2 = alloca i32, align 4 store i32 %0, i32* %2, align 4 %3 = load i32, i32* %2, align 4 %4 = add nsw i32 %3, 1 store i32 %4, i32* %2, align 4 ret i32 %4 } clangの気持ちの調べ方 LLVM中間コードを吐く 中間コードを 読み解く
  7. 15 19 clang++の気持ち int a = 1; int b =

    ++a + ++a; int a = 1; int tmp1 = a + 1; a = tmp1; int b = tmp1 + ++a; int a = 1; int tmp1 = a + 1; a = tmp1; int tmp2 = a + 1; a = tmp2; int b = tmp1 + tmp2; ++aを再帰的に tmp = a + 1; に展開
  8. 16 19 g++の気持ち int func(int a){ return ++a + ++a;

    } GCCの気持ちの調べ方 $ gcc -c -fdump-tree-all test.c int func (int a) { int D.2719; a = a + 1; a = a + 1; D.2719 = a * 2; return D.2719; } GIMPLE中間表現を吐く 中間表現を 読み解く
  9. 17 19 g++の気持ち int a = 1; int b =

    ++a + ++a; int a = 1; a = a + 1; a = a + 1; tmp = a + a int b = tmp; 二項演算子 (++a + ++a) ごとに再帰的に展開 ++a + ++a + ++a (++a + ++a) + ++a tmp = (++a + ++a) tmp + ++a 複数あったら 二個ずつ処理
  10. 18 19 まとめ • 同じソースでclang++とg++で異なる動作をする コードを書き、Fizz Buzzもどきをやってみた • 他の言語処理系でも起きる •

    clang派(PHP, D, JavaScript) • GCC派(Perl) • 多くの後発言語はC/C++を反面教師として設計さ れている • インクリメント演算子廃止(Ruby, Python) • 後置のみにして文に(Go) • 未定義動作について学ぶと、言語処理系について の理解が進む
  11. 19 19 オ レ は よ う や く の

    ぼ り は じ め た ば か り だ か ら な こ の は て し な く 遠 い コ ン パ イ ラ い じ め 道 を よ …