ILでFizzBuzzを書く
.NETをコンパイルするときの中間言語(IL)を直接書き下してFizzBuzzを書いてみます。
メモ
ldc.i4//int32ロード ldloc//ローカル変数のロード ldstr//文字列のロード call//メソッド呼び出し stloc//ローカル変数にストア bgt//value1 > value2なら分岐 rem//剰余 brfalse//0,nullなら分岐 box//値型をボクシング br//強制分岐
c#で書くと
int i, n; Console.Write("fizzbuzz max>"); n = Int32.Parse(Console.ReadLine()); for (i = 0; i < n; i++) { if (i % 15 == 0) { Console.WriteLine("FizzBuzz"); } else if (i % 5 == 0) { Console.WriteLine("Buzz"); } else if (i % 3 == 0) { Console.WriteLine("Fizz"); } else { Console.WriteLine(i); } }
微妙に固く書きましたがILと似たような感じ(ILは表示させる物をobjectに格納してConsole.WriteLineに投げてる
IL DASMでばらしてみると
.method private hidebysig static void Main() cil managed { .entrypoint // コード サイズ 136 (0x88) .maxstack 2 .locals init ([0] int32 i, [1] int32 n, [2] bool CS$4$0000) IL_0000: nop IL_0001: ldstr "fizzbuzz max>" IL_0006: call void [mscorlib]System.Console::Write(string) IL_000b: nop IL_000c: call string [mscorlib]System.Console::ReadLine() IL_0011: call int32 [mscorlib]System.Int32::Parse(string) IL_0016: stloc.1 IL_0017: ldc.i4.0 IL_0018: stloc.0 IL_0019: br.s IL_007f IL_001b: nop IL_001c: ldloc.0 IL_001d: ldc.i4.s 15 IL_001f: rem IL_0020: ldc.i4.0 IL_0021: ceq IL_0023: ldc.i4.0 IL_0024: ceq IL_0026: stloc.2 IL_0027: ldloc.2 IL_0028: brtrue.s IL_0039 IL_002a: nop IL_002b: ldstr "FizzBuzz" IL_0030: call void [mscorlib]System.Console::WriteLine(string) IL_0035: nop IL_0036: nop IL_0037: br.s IL_007a IL_0039: ldloc.0 IL_003a: ldc.i4.5 IL_003b: rem IL_003c: ldc.i4.0 IL_003d: ceq IL_003f: ldc.i4.0 IL_0040: ceq IL_0042: stloc.2 IL_0043: ldloc.2 IL_0044: brtrue.s IL_0055 IL_0046: nop IL_0047: ldstr "Buzz" IL_004c: call void [mscorlib]System.Console::WriteLine(string) IL_0051: nop IL_0052: nop IL_0053: br.s IL_007a IL_0055: ldloc.0 IL_0056: ldc.i4.3 IL_0057: rem IL_0058: ldc.i4.0 IL_0059: ceq IL_005b: ldc.i4.0 IL_005c: ceq IL_005e: stloc.2 IL_005f: ldloc.2 IL_0060: brtrue.s IL_0071 IL_0062: nop IL_0063: ldstr "Fizz" IL_0068: call void [mscorlib]System.Console::WriteLine(string) IL_006d: nop IL_006e: nop IL_006f: br.s IL_007a IL_0071: nop IL_0072: ldloc.0 IL_0073: call void [mscorlib]System.Console::WriteLine(int32) IL_0078: nop IL_0079: nop IL_007a: nop IL_007b: ldloc.0 IL_007c: ldc.i4.1 IL_007d: add IL_007e: stloc.0 IL_007f: ldloc.0 IL_0080: ldloc.1 IL_0081: clt IL_0083: stloc.2 IL_0084: ldloc.2 IL_0085: brtrue.s IL_001b IL_0087: ret } // end of method Program::Main
IL_001c
~ IL_0028
を見ればわかるのですが、
i % 15 == 0 == 0 結果を一度変数に格納 割り切れてなかったらジャンプ
のような一見無駄がありそうなコードに変換されているようにもみえます。それとelseでジャンプするようにもなっています。
ちなみにILで書いたコードもビルドするなりプロジェクトに加えてexternするなりすればc#コードと混在して使用できます。