[解決済み] スイッチケース組立レベルコード
質問
WindowsのcygwinでC言語のプログラミングをしています。C言語でのプログラミングに少し慣れてきたところで、フードの下を見て、私が書いたコードに対してコンパイラが何をしているのか見てみたくなったのです。
そこで、switch case文を含むコードブロックを書き下ろし、usingでアセンブリに変換してみました。
gcc -S foo.c
以下はC言語のソースです。
switch(i)
{
case 1:
{
printf("Case 1\n");
break;
}
case 2:
{ printf("Case 2\n");
break;
}
case 3:
{
printf("Case 3\n");
break;
}
case 4:
{
printf("Case 4\n");
break;
}
case 5:
{
printf("Case 5\n");
break;
}
case 6:
{
printf("Case 6\n");
break;
}
case 7:
{
printf("Case 7\n");
break;
}
case 8:
{
printf("Case 8\n");
break;
}
case 9:
{
printf("Case 9\n");
break;
}
case 10:
{
printf("Case 10\n");
break;
}
default:
{
printf("Nothing\n");
break;
}
}
さて、同じように出来上がったアセンブリはというと。
movl $5, -4(%ebp)
cmpl $10, -4(%ebp)
ja L13
movl -4(%ebp), %eax
sall $2, %eax
movl L14(%eax), %eax
jmp *%eax
.section .rdata,"dr"
.align 4
L14:
.long L13
.long L3
.long L4
.long L5
.long L6
.long L7
.long L8
.long L9
.long L10
.long L11
.long L12
.text
L3:
movl $LC0, (%esp)
call _printf
jmp L2
L4:
movl $LC1, (%esp)
call _printf
jmp L2
L5:
movl $LC2, (%esp)
call _printf
jmp L2
L6:
movl $LC3, (%esp)
call _printf
jmp L2
L7:
movl $LC4, (%esp)
call _printf
jmp L2
L8:
movl $LC5, (%esp)
call _printf
jmp L2
L9:
movl $LC6, (%esp)
call _printf
jmp L2
L10:
movl $LC7, (%esp)
call _printf
jmp L2
L11:
movl $LC8, (%esp)
call _printf
jmp L2
L12:
movl $LC9, (%esp)
call _printf
jmp L2
L13:
movl $LC10, (%esp)
call _printf
L2:
さて、アセンブリでは、コードはまず最後のケース(つまりケース10)を最初にチェックします。これは非常に奇妙なことです。そして、'i'を'eax'にコピーして、私には理解できないことをやっているのです。
コンパイラはswitch..caseのために何らかのジャンプテーブルを実装していると聞いたことがあります。このコードがやっていることはそれなのでしょうか?あるいは、このコードがやっていることとその理由は何でしょうか?なぜなら、ケースの数が少ない場合。 というのは、if...else のラダーで生成されるコードとほとんど変わらないのですが、ケース数が増えてくると、このような変わった実装が見られるようになります。
よろしくお願いします。
どのように解決するのですか?
まず、i と 10 を比較して、値が 10 より大きい場合にデフォルトのケースにジャンプするコードです (
cmpl $10, -4(%ebp)
に続いて
ja L13
).
次のコードでは、入力を左に2つシフトしています(
sall $2, %eax
これは、ジャンプテーブルのオフセットを生成する4倍と同じです(テーブルの各エントリは4バイト長であるため)。
そして、ジャンプテーブルからアドレスをロードします(
movl L14(%eax), %eax
) にジャンプし、その場所へ (
jmp *%eax
).
ジャンプテーブルは、単純にアドレス(アセンブリコードではラベルで表現される)のリストです。
L14:
.long L13
.long L3
.long L4
...
一つ注目すべきは
L13
はデフォルトのケースを表しています。 これは、ジャンプテーブルの最初のエントリ(iが0の場合)であると同時に、最初(i > 10の場合)に特別に処理されるものです。
関連
-
[解決済み】単項演算子「*」の型が無効(「int」がある)C言語でのエラー
-
[解決済み] Connect: ソケット以外でのソケット操作
-
[解決済み】ISO C90では、C言語での宣言とコードの混在が禁止されています。
-
[解決済み】C言語でint64_t型を表示する方法
-
[解決済み】argv[]をint型として取得するには?
-
[解決済み】なぜか。"エラー: 配列型を持つ式への代入"
-
[解決済み】Makefile:1: ***セパレータがありません。停止します。
-
[解決済み] C言語のコードで「:-!」とは何ですか?
-
[解決済み] 配列の場合、なぜ a[5] == 5[a] になるのでしょうか?
-
[解決済み] Collatz予想の検証を行うC++のコードは、なぜ手書きのアセンブリよりも高速に動作するのでしょうか?
最新
-
nginxです。[emerg] 0.0.0.0:80 への bind() に失敗しました (98: アドレスは既に使用中です)
-
htmlページでギリシャ文字を使うには
-
ピュアhtml+cssでの要素読み込み効果
-
純粋なhtml + cssで五輪を実現するサンプルコード
-
ナビゲーションバー・ドロップダウンメニューのHTML+CSSサンプルコード
-
タイピング効果を実現するピュアhtml+css
-
htmlの選択ボックスのプレースホルダー作成に関する質問
-
html css3 伸縮しない 画像表示効果
-
トップナビゲーションバーメニュー作成用HTML+CSS
-
html+css 実装 サイバーパンク風ボタン
おすすめ
-
[解決済み】組み込み関数「malloc」の暗黙の宣言の非互換性
-
[解決済み] strtokのセグメンテーションフォールト
-
[解決済み] clang: error: linker command failed with exit code 1が表示されるのはなぜですか?
-
[解決済み】cudamalloc()の使用。) なぜダブルポインタなのか?
-
[解決済み】C言語で多重定義を防ぐには?
-
[解決済み】malloc():メモリ破壊
-
[解決済み】0LLや0x0ULの意味は何ですか?
-
[解決済み】宣言指定子で2つ以上のデータ型がある場合のエラー【非公開
-
[解決済み】makefile:4。*** missing separator. 停止する
-
[解決済み] Cプログラムで「配列の添え字が整数でない」。