C语言 开关盒装配级代码
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/3012011/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Switch case assembly level code
提问by puffadder
I am programming C on cygwin windows. After having done a bit of C programming and getting comfortable with the language, I wanted to look under the hood and see what the compiler is doing for the code that I write.
我正在 cygwin windows 上编程 C。在完成了一些 C 编程并熟悉了该语言之后,我想深入了解一下编译器为我编写的代码做了什么。
So I wrote down a code block containing switch case statements and converted them into assembly using:
所以我写下了一个包含 switch case 语句的代码块,并使用以下方法将它们转换为汇编:
gcc -S foo.c
Here is the C source:
这是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;
}
}
Now the resultant assembly for the same is:
现在相同的结果程序集是:
movl , -4(%ebp)
cmpl , -4(%ebp)
ja L13
movl -4(%ebp), %eax
sall , %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:
Now, in the assembly, the code is first checking the last case (i.e. case 10) first. This is very strange. And then it is copying 'i' into 'eax' and doing things that are beyond me.
现在,在程序集中,代码首先检查最后一个案例(即案例 10)。这很奇怪。然后它将“i”复制到“eax”并做一些超出我的事情。
I have heard that the compiler implements some jump table for switch..case. Is it what this code is doing? Or what is it doing and why? Because in case of less number of cases, the code is pretty similar to that generated for if...else ladder, but when number of cases increases, this unusual-looking implementation is seen.
我听说编译器为 switch..case 实现了一些跳转表。是这段代码在做什么吗?或者它在做什么,为什么?因为在案例数量较少的情况下,代码与为 if...else 阶梯生成的代码非常相似,但是当案例数量增加时,就会看到这种看起来不寻常的实现。
Thanks in advance.
提前致谢。
回答by R Samuel Klatchko
First the code is comparing the i to 10 and jumping to the default case when the value is greater then 10 (cmpl $10, -4(%ebp)followed by ja L13).
首先,代码将 i 与 10 进行比较,并在值大于 10(cmpl $10, -4(%ebp)后跟ja L13)时跳转到默认情况。
The next bit of code is shifting the input to the left by two (sall $2, %eax) which is the same as multiple by four which generates an offset into the jump table (because each entry in the table is 4 bytes long)
下一位代码将输入向左移动 2 ( sall $2, %eax),这与乘以 4 的倍数相同,这会生成跳转表的偏移量(因为表中的每个条目长 4 个字节)
It then loads an address from the jump table (movl L14(%eax), %eax) and jumps to it (jmp *%eax).
然后它从跳转表 ( movl L14(%eax), %eax)加载一个地址并跳转到它 ( jmp *%eax)。
The jump table is simply a list of addresses (represented in assembly code by labels):
跳转表只是一个地址列表(在汇编代码中用标签表示):
L14:
.long L13
.long L3
.long L4
...
One thing to notice is that L13represents the default case. It is both the first entry in the jump table (for when i is 0) and is handled specially at the beginning (when i > 10).
需要注意的一件事是L13代表默认情况。它既是跳转表中的第一个条目(当 i 为 0 时),又在开始时(当 i > 10 时)进行特殊处理。
回答by ShinTakezou
Yes it is a jump table. The first checking is to check if the value is in the cases and jump to default if it is not. Don't forget that in such a table, if %eax is 0, L14(%eax) points to the first element of the table (L13). So in the table the case 10:is indexed with 9, not 10.
是的,它是一个跳转表。第一个检查是检查值是否在 case 中,如果不在,则跳转到默认值。不要忘记,在这样的表中,如果 %eax 为 0,则 L14(%eax) 指向表的第一个元素 (L13)。所以在表case 10:中索引为 9,而不是 10。
The way the switch can be done depends on the values you have in case; in this case they are in "sequence", so the simple jump table is possible.
切换的方式取决于您在 中的值case;在这种情况下,它们是“顺序”的,所以简单的跳转表是可能的。
回答by Nicolas Viennot
For [1..10] the compiler will generate a table so that it doesn't need to compare the value to go somewhere, it directly do a: goto table[i]. That way it's faster.
对于 [ 1..10] 编译器将生成一个表,这样它就不需要比较值到某处,它直接执行 a: goto table[i]。这样速度更快。
But in case i > 10it jumps to your default statement. It has to check first before jumping otherwise, the program would miserably crash.
但万一i > 10它跳转到您的默认语句。它必须在跳转之前先检查,否则程序会崩溃。
If you had sparse values (like, 23, 9233, 91238, and not 1, 2, 3...), the compiler would not generate such a table, and compare each value.
如果您有稀疏值(例如 23、9233、91238,而不是 1、2、3...),编译器将不会生成这样的表,并比较每个值。
回答by stacker
Yes, first eax is calculated by the switch value (sallshift as multiplication) to get the adress from the jump table (following label L14:)
是的,首先 eax 是通过开关值(sall移位为乘法)计算的,以从跳转表(以下标签L14:)中获取地址
jmp *%eaxis a near jump to the label of your case.
(jmp near eax)
jmp *%eax几乎跳转到您的案例标签。(jmp 靠近 eax)
The code following the other labels is just printing and skips the other cases.
其他标签后面的代码只是打印并跳过其他情况。

