簡體   English   中英

切換機箱組件級別代碼

[英]Switch case assembly level code

我在cygwin windows上編寫C語言。 在完成了一些C編程並熟悉語言之后,我想深入了解編譯器為我編寫的代碼做了些什么。

所以我寫下了一個包含switch case語句的代碼塊,並使用以下方法將它們轉換為匯編:

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)。 這很奇怪。 然后它將'我'復制到'eax'並做一些超出我的事情。

我聽說編譯器為switch..case實現了一些跳轉表。 這個代碼在做什么? 或者它在做什么以及為什么? 因為在案例數較少的情況下,代碼與if ... else梯形圖的代碼非常相似,但是當案例數增加時,可以看到這種看似不尋常的實現。

提前致謝。

首先,代碼將i與10進行比較,並在值大於10時跳轉到默認情況( cmpl $10, -4(%ebp)后跟ja L13 )。

下一位代碼將輸入向左移動兩個( sall $2, %eax ),它與乘以4的倍數相同,后者在跳轉表中生成一個偏移量(因為表中的每個條目長度為4個字節)

然后它從跳轉表( movl L14(%eax), %eax )加載一個地址並跳轉到它( jmp *%eax )。

跳轉表只是一個地址列表(按標簽用匯編代碼表示):

L14:
.long   L13
.long   L3
.long   L4
...

需要注意的一點是, L13代表默認情況。 它既是跳轉表中的第一個條目(當我為0時)並且在開始時特別處理(當i> 10時)。

是的,它是一個跳轉表。 第一個檢查是檢查值是否在案例中,如果不是則跳轉到默認值。 不要忘記在這樣的表中,如果%eax為0,則L14(%eax)指向表的第一個元素(L13)。 所以在表格中, case 10:用9而不是10來索引。

切換的方式取決於你的case ; 在這種情況下,它們處於“序列”中,因此簡單的跳轉表是可能的。

對於[ 1..10 ],編譯器將生成一個表,以便它不需要比較值去某處,它直接執行: goto table[i] 這樣它更快。

但是如果i > 10它會跳轉到您的默認語句。 它必須在跳躍之前先檢查,否則程序會崩潰。

如果您有稀疏值(例如,23,9233,91238,而不是1,2,3 ......),編譯器將不會生成這樣的表,並比較每個值。

是的,第一個eax是通過開關值( sall shift as multiplication)來計算的,以從跳轉表中獲取地址(在標簽L14:

jmp *%eax是一個接近你的案件標簽的跳轉。 (靠近eax的jmp)

其他標簽后面的代碼只是打印並跳過其他情況。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM