<ruby id="bdb3f"></ruby>

    <p id="bdb3f"><cite id="bdb3f"></cite></p>

      <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
        <p id="bdb3f"><cite id="bdb3f"></cite></p>

          <pre id="bdb3f"></pre>
          <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

          <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
          <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

          <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                <ruby id="bdb3f"></ruby>

                ??一站式輕松地調用各大LLM模型接口,支持GPT4、智譜、豆包、星火、月之暗面及文生圖、文生視頻 廣告
                # 第11章 選擇結構switch()/case/default ## 11.1 一些例子 ``` #!bash void f (int a) { switch (a) { case 0: printf ("zero "); break; case 1: printf ("one "); break; case 2: printf ("two "); break; default: printf ("something unknown "); break; }; }; ``` ### 11.1.1 X86 反匯編結果如下(MSVC 2010): 清單11.1: MSVC 2010 ``` #!bash tv64 = -4 ; size = 4 _a$ = 8 ; size = 4 _f PROC push ebp mov ebp, esp push ecx mov eax, DWORD PTR _a$[ebp] mov DWORD PTR tv64[ebp], eax cmp DWORD PTR tv64[ebp], 0 je SHORT $LN4@f cmp DWORD PTR tv64[ebp], 1 je SHORT $LN3@f cmp DWORD PTR tv64[ebp], 2 je SHORT $LN2@f jmp SHORT $LN1@f $LN4@f: push OFFSET $SG739 ; ’zero’, 0aH, 00H call _printf add esp, 4 jmp SHORT $LN7@f $LN3@f: push OFFSET $SG741 ; ’one’, 0aH, 00H call _printf add esp, 4 jmp SHORT $LN7@f $LN2@f: push OFFSET $SG743 ; ’two’, 0aH, 00H call _printf add esp, 4 jmp SHORT $LN7@f $LN1@f: push OFFSET $SG745 ; ’something unknown’, 0aH, 00H call _printf add esp, 4 $LN7@f: mov esp, ebp pop ebp ret 0 _f ENDP ``` 輸出函數的switch中有一些case選擇分支,事實上,它是和下面這個形式等價的: ``` #!cpp void f (int a) { if (a==0) printf ("zero "); else if (a==1) printf ("one "); else if (a==2) printf ("two "); else printf ("something unknown "); }; ``` 當switch()中有一些case分支時,我們可以看到此類代碼,雖然不能確定,但是,事實上switch()在機器碼級別上就是對if()的封裝。這也就是說,switch()其實只是對有一大堆類似條件判斷的if()的一個語法糖。 在生成代碼時,除了編譯器把輸入變量移動到一個臨時本地變量tv64中之外,這塊代碼對我們來說并無新意。 如果是在GCC 4.4.1下編譯同樣的代碼,我們得到的結果也幾乎一樣,即使你打開了最高優化(-O3)也是如此。 讓我們在微軟VC編譯器中打開/Ox優化選項: cl 1.c /Fa1.asm /Ox 清單11.2: MSVC ``` #!bash _a$ = 8 ; size = 4 _f PROC mov eax, DWORD PTR _a$[esp-4] sub eax, 0 je SHORT $LN4@f sub eax, 1 je SHORT $LN3@f sub eax, 1 je SHORT $LN2@f mov DWORD PTR _a$[esp-4], OFFSET $SG791 ; ’something unknown’, 0aH, 00H jmp _printf $LN2@f: mov DWORD PTR _a$[esp-4], OFFSET $SG789 ; ’two’, 0aH, 00H jmp _printf $LN3@f: mov DWORD PTR _a$[esp-4], OFFSET $SG787 ; ’one’, 0aH, 00H jmp _printf $LN4@f: mov DWORD PTR _a$[esp-4], OFFSET $SG785 ; ’zero’, 0aH, 00H jmp _printf _f ENDP ``` 我們可以看到瀏覽器做了更多的難以閱讀的優化(Dirty hacks)。 首先,變量的值會被放入EAX,接著EAX減0。聽起來這很奇怪,但它之后是需要檢查先前EAX寄存器的值是否為0的,如果是,那么程序會設置上零標志位ZF(這也表示了減去0之后,結果依然是0),第一個條件跳轉語句JE(Jump if Equal 或者同義詞 JZ - Jump if Zero)會因此觸發跳轉。如果這個條件不滿足,JE沒有跳轉的話,輸入值將減去1,之后就和之前的一樣了,如果哪一次值是0,那么JE就會觸發,從而跳轉到對應的處理語句上。 (譯注:SUB操作會重置零標志位ZF,但是MOV不會設置標志位,而JE將只有在ZF標志位設置之后才會跳轉。如果需要基于EAX的值來做JE跳轉的話,是需要用這個方法設置標志位的)。 并且,如果沒有JE語句被觸發,最終,printf()函數將收到“something unknown”的參數。 其次:我們看到了一些不尋常的東西——字符串指針被放在了變量里,然后printf()并沒有通過CALL,而是通過JMP來調用的。 這個可以很簡單的解釋清楚,調用者把參數壓棧,然后通過CALL調用函數。CALL通過把返回地址壓棧,然后做無條件跳轉來跳到我們的函數地址。我們的函數在執行時,不管在任何時候都有以下的棧結構(因為它沒有任何移動棧指針的語句): ``` · ESP —— 指向返回地址 · ESP+4 —— 指向變量a (也即參數) ``` 另一方面,當我們這兒調用printf()函數的時候,它也需要有與我們這個函數相同的棧結構,不同之處只在于printf()的第一個參數是指向一個字符串的。 這也就是你之前看到的我們的代碼所做的事情。 我們的代碼把第一個參數的地址替換了,然后跳轉到printf(),就像第一個沒有調用我們的函數f()而是先調用了printf()一樣。 printf()把一串字符輸出到stdout 中,然后執行RET語句, 這一句會從棧上彈出返回地址,因此,此時控制流會返回到調用f()的函數上,而不是f()上。 這一切之所以能發生,是因為printf()在f()的末尾。在一些情況下,這有些類似于longjmp()函數。當然,這一切只是為了提高執行速度。 ARM編譯器也有類似的優化,請見5.3.2節“帶有多個參數的printf()函數調用”。 ### 11.1.2 ARM: 優化后的 Keil + ARM 模式 ``` #!bash .text:0000014C f1 .text:0000014C 00 00 50 E3 CMP R0, #0 .text:00000150 13 0E 8F 02 ADREQ R0, aZero ; "zero " .text:00000154 05 00 00 0A BEQ loc_170 .text:00000158 01 00 50 E3 CMP R0, #1 .text:0000015C 4B 0F 8F 02 ADREQ R0, aOne ; "one " .text:00000160 02 00 00 0A BEQ loc_170 .text:00000164 02 00 50 E3 CMP R0, #2 .text:00000168 4A 0F 8F 12 ADRNE R0, aSomethingUnkno ; "something unknown " .text:0000016C 4E 0F 8F 02 ADREQ R0, aTwo ; "two " .text:00000170 .text:00000170 loc_170 ; CODE XREF: f1+8 .text:00000170 ; f1+14 .text:00000170 78 18 00 EA B __2printf ``` 我們再一次看看這個代碼,我們不能確定的說這就是源代碼里面的switch()或者說它是if()的封裝。 但是,我們可以看到這里它也在試圖預測指令(像是ADREQ(相等)),這里它會在R0=0的情況下觸發,并且字符串“zero”的地址將被加載到R0中。如果R0=0,下一個指令BEQ將把控制流定向到loc_170處。順帶一說,機智的讀者們可能會文,之前的ADREQ已經用其他值填充了R0寄存器了,那么BEQ會被正確觸發嗎?答案是“是”。因為BEQ檢查的是CMP所設置的標記位,但是ADREQ根本沒有修改標記位。 還有,在ARM中,一些指令還會加上-S后綴,這表明指令將會根據結果設置標記位。如果沒有-S的話,表明標記位并不會被修改。比如,ADD(而不是ADDS)將會把兩個操作數相加,但是并不會涉及標記位。這類指令對使用CMP設置標記位之后使用標記位的指令,例如條件跳轉來說非常有用。 其他指令對我們來說已經很熟悉了。這里只有一個調用指向printf(),在末尾,我們已經知道了這個小技巧(見5.3.2節)。在末尾處有三個指向printf()的地址。 還有,需要注意的是如果a=2但是a并不在它的選擇分支給定的常數中時,“CMP R0, #2”指令在這個情況下就需要知道a是否等于2。如果結果為假,ADRNE將會讀取字符串“something unknown ”到R0中,因為a在之前已經和0、1做過是否相等的判斷了,這里我們可以假定a并不等于0或者1。并且,如果R0=2,a指向的字符串“two ”將會被ADREQ載入R0。 ### 11.1.3 ARM: 優化后的 Keil + thumb 模式 ``` #!bash .text:000000D4 f1 .text:000000D4 10 B5 PUSH {R4,LR} .text:000000D6 00 28 CMP R0, #0 .text:000000D8 05 D0 BEQ zero_case .text:000000DA 01 28 CMP R0, #1 .text:000000DC 05 D0 BEQ one_case .text:000000DE 02 28 CMP R0, #2 .text:000000E0 05 D0 BEQ two_case .text:000000E2 91 A0 ADR R0, aSomethingUnkno ; "something unknown " .text:000000E4 04 E0 B default_case .text:000000E6 ; ------------------------------------------------------------------------- .text:000000E6 zero_case ; CODE XREF: f1+4 .text:000000E6 95 A0 ADR R0, aZero ; "zero " .text:000000E8 02 E0 B default_case .text:000000EA ; ------------------------------------------------------------------------- .text:000000EA one_case ; CODE XREF: f1+8 .text:000000EA 96 A0 ADR R0, aOne ; "one " .text:000000EC 00 E0 B default_case .text:000000EE ; ------------------------------------------------------------------------- .text:000000EE two_case ; CODE XREF: f1+C .text:000000EE 97 A0 ADR R0, aTwo ; "two " .text:000000F0 default_case ; CODE XREF: f1+10 .text:000000F0 ; f1+14 .text:000000F0 06 F0 7E F8 BL __2printf .text:000000F4 10 BD POP {R4,PC} .text:000000F4 ; End of function f1 ``` 正如我之前提到的,在thumb模式下并沒有什么功能來連接預測結果,所以這里的thumb代碼有點像容易理解的x86 CISC代碼。 ## 11.2 許多例子 在有許多case分支的switch()語句中,對編譯器來說,轉換出一大堆JE/JNE語句并不是太方便。 ``` #!cpp void f (int a) { switch (a) { case 0: printf ("zero "); break; case 1: printf ("one "); break; case 2: printf ("two "); break; case 3: printf ("three "); break; case 4: printf ("four "); break; default: printf ("something unknown "); break; }; }; ``` ### 11.2.1 x86 反匯編結果如下(MSVC 2010): 清單11.3: MSVC 2010 ``` #!bash tv64 = -4 ; size = 4 _a$ = 8 ; size = 4 _f PROC push ebp mov ebp, esp push ecx mov eax, DWORD PTR _a$[ebp] mov DWORD PTR tv64[ebp], eax cmp DWORD PTR tv64[ebp], 4 ja SHORT $LN1@f mov ecx, DWORD PTR tv64[ebp] jmp DWORD PTR $LN11@f[ecx*4] $LN6@f: push OFFSET $SG739 ; ’zero’, 0aH, 00H call _printf add esp, 4 jmp SHORT $LN9@f $LN5@f: push OFFSET $SG741 ; ’one’, 0aH, 00H call _printf add esp, 4 jmp SHORT $LN9@f $LN4@f: push OFFSET $SG743 ; ’two’, 0aH, 00H call _printf add esp, 4 jmp SHORT $LN9@f $LN3@f: push OFFSET $SG745 ; ’three’, 0aH, 00H call _printf add esp, 4 jmp SHORT $LN9@f $LN2@f: push OFFSET $SG747 ; ’four’, 0aH, 00H call _printf add esp, 4 jmp SHORT $LN9@f $LN1@f: push OFFSET $SG749 ; ’something unknown’, 0aH, 00H call _printf add esp, 4 $LN9@f: mov esp, ebp pop ebp ret 0 npad 2 $LN11@f: DD $LN6@f ; 0 DD $LN5@f ; 1 DD $LN4@f ; 2 DD $LN3@f ; 3 DD $LN2@f ; 4 _f ENDP ``` 好的,我們可以看到這兒有一組不同參數的printf()調用。 它們不僅有內存中的地址,編譯器還給它們帶上了符號信息。順帶一提,這些符號標簽也都存在于$LN11@f內部函數表中。 在函數最開始,如果a大于4,控制流將會被傳遞到標簽$LN1@f上,這兒會有一個參數為“something unknown”的printf()調用。 如果a值小于等于4,然后我們把它乘以4,加上$LN1@f的函數地址。這就是在函數表內部構造地址的方法,這樣可以正好指向我們需要的元素。比如a等于2。 那么,2×4=8(在32位進程下,所有的函數表元素的長度都只有4字節),$LN11@f的函數表地址+8——這樣就能取得$LN4@f標簽的位置。 JMP將從函數表中獲得$LN4@f的地址,然后跳轉向它。 這個函數表,有時候也叫做跳轉表(jumptable)。 然后,對應的,printf()的參數就是“two”了。 字面意思, JMP DWORD PTR $LN11@f[ECX*4] 指令意味著“ 跳轉到存儲在$LN11@f + ecx * 4 地址上的雙字”。 npad(64)是一個編譯時語言宏,它用于對齊下一個標簽,這樣存儲的地址就會按照4字節(或者16字節)對齊。這個對于處理器來說是十分合適的,因為通過內存總線、緩存從內存中獲取32位的值是非常方便而且有效率的。 讓我們看看GCC 4.4.1 生成的代碼: 清單11.4: GCC 4.4.1 ``` #!bash public f f proc near ; CODE XREF: main+10 var_18 = dword ptr -18h arg_0 = dword ptr 8 push ebp mov ebp, esp sub esp, 18h ; char * cmp [ebp+arg_0], 4 ja short loc_8048444 mov eax, [ebp+arg_0] shl eax, 2 mov eax, ds:off_804855C[eax] jmp eax loc_80483FE: ; DATA XREF: .rodata:off_804855C mov [esp+18h+var_18], offset aZero ; "zero" call _puts jmp short locret_8048450 loc_804840C: ; DATA XREF: .rodata:08048560 mov [esp+18h+var_18], offset aOne ; "one" call _puts jmp short locret_8048450 loc_804841A: ; DATA XREF: .rodata:08048564 mov [esp+18h+var_18], offset aTwo ; "two" call _puts jmp short locret_8048450 loc_8048428: ; DATA XREF: .rodata:08048568 mov [esp+18h+var_18], offset aThree ; "three" call _puts jmp short locret_8048450 loc_8048436: ; DATA XREF: .rodata:0804856C mov [esp+18h+var_18], offset aFour ; "four" call _puts jmp short locret_8048450 loc_8048444: ; CODE XREF: f+A mov [esp+18h+var_18], offset aSomethingUnkno ; "something unknown" call _puts locret_8048450: ; CODE XREF: f+26 ; f+34... leave retn f endp off_804855C dd offset loc_80483FE ; DATA XREF: f+12 dd offset loc_804840C dd offset loc_804841A dd offset loc_8048428 dd offset loc_8048436 ``` 基本和VC生成的相同,除了少許的差別:參數arg_0的乘以4操作被左移2位替換了(這集合和乘以4一樣)(見17.3.1節)。 然后標簽地址從off_804855C處的數組獲取,地址計算之后存儲到EAX中,然后通過JMP EAX跳轉到實際的地址上。 ### 11.2.2 ARM: 優化后的 Keil + ARM 模式 ``` #!bash 00000174 f2 00000174 05 00 50 E3 CMP R0, #5 ; switch 5 cases 00000178 00 F1 8F 30 ADDCC PC, PC, R0,LSL#2 ; switch jump 0000017C 0E 00 00 EA B default_case ; jumptable 00000178 default case 00000180 ; ------------------------------------------------------------------------- 00000180 00000180 loc_180 ; CODE XREF: f2+4 00000180 03 00 00 EA B zero_case ; jumptable 00000178 case 0 00000184 ; ------------------------------------------------------------------------- 00000184 00000184 loc_184 ; CODE XREF: f2+4 00000184 04 00 00 EA B one_case ; jumptable 00000178 case 1 00000188 ; ------------------------------------------------------------------------- 00000188 00000188 loc_188 ; CODE XREF: f2+4 00000188 05 00 00 EA B two_case ; jumptable 00000178 case 2 0000018C ; ------------------------------------------------------------------------- 0000018C 0000018C loc_18C ; CODE XREF: f2+4 0000018C 06 00 00 EA B three_case ; jumptable 00000178 case 3 00000190 ; ------------------------------------------------------------------------- 00000190 00000190 loc_190 ; CODE XREF: f2+4 00000190 07 00 00 EA B four_case ; jumptable 00000178 case 4 00000194 ; ------------------------------------------------------------------------- 00000194 00000194 zero_case ; CODE XREF: f2+4 00000194 ; f2:loc_180 00000194 EC 00 8F E2 ADR R0, aZero ; jumptable 00000178 case 0 00000198 06 00 00 EA B loc_1B8 0000019C ; ------------------------------------------------------------------------- 0000019C 0000019C one_case ; CODE XREF: f2+4 0000019C ; f2:loc_184 0000019C EC 00 8F E2 ADR R0, aOne ; jumptable 00000178 case 1 000001A0 04 00 00 EA B loc_1B8 000001A4 ; ------------------------------------------------------------------------- 000001A4 000001A4 two_case ; CODE XREF: f2+4 000001A4 ; f2:loc_188 000001A4 01 0C 8F E2 ADR R0, aTwo ; jumptable 00000178 case 2 000001A8 02 00 00 EA B loc_1B8 000001AC ; ------------------------------------------------------------------------- 000001AC 000001AC three_case ; CODE XREF: f2+4 000001AC ; f2:loc_18C 000001AC 01 0C 8F E2 ADR R0, aThree ; jumptable 00000178 case 3 000001B0 00 00 00 EA B loc_1B8 000001B4 ; ------------------------------------------------------------------------- 000001B4 000001B4 four_case ; CODE XREF: f2+4 000001B4 ; f2:loc_190 000001B4 01 0C 8F E2 ADR R0, aFour ; jumptable 00000178 case 4 000001B8 000001B8 loc_1B8 ; CODE XREF: f2+24 000001B8 ; f2+2C 000001B8 66 18 00 EA B __2printf 000001BC ; ------------------------------------------------------------------------- 000001BC 000001BC default_case ; CODE XREF: f2+4 000001BC ; f2+8 000001BC D4 00 8F E2 ADR R0, aSomethingUnkno ; jumptable 00000178 default case 000001C0 FC FF FF EA B loc_1B8 000001C0 ; End of function f2 ``` 這個代碼利用了ARM的特性,這里ARM模式下所有指令都是4個字節。 讓我們記住a的最大值是4,任何更大額值都會導致它輸出“something unknown ”。 最開始的“CMP R0, #5”指令將a的值與5比較。 下一個“ADDCC PC, PC, R0, LSL#2”指令將僅在R0&lt;5的時候執行(CC = Carry clear , 小于)。所以,如果ADDCC并沒有觸發(R0&gt;=5時),它將會跳轉到default _case標簽上。 但是,如果R0&lt;5,而且ADDCC觸發了,將會發生下列事情: R0中的值會乘以4,事實上,LSL#2代表著“左移2位”,但是像我們接下來(見17.3.1節)要看到的“移位”一樣,左移2位代表乘以4。 然后,我們得到了R0 * 4的值,這個值將會和PC中現有的值相加,因此跳轉到下述其中一個B(Branch 分支)指令上。 在ADDCC執行時,PC中的值(0x180)比ADDCC指令的值(0x178)提前8個字節,換句話說,提前2個指令。 這也就是為ARM處理器通道工作的方式:當ADDCC指令執行的時候,此時處理器將開始處理下一個指令,這也就是PC會指向這里的原因。 如果a=0,那么PC將不會和任何值相加,PC中實際的值將寫入PC中(它相對之領先8個字節),然后跳轉到標簽loc_180處。這就是領先ADDCC指令8個字節的地方。 在a=1時,PC+8+a_4 = PC+8+1_4 = PC+16= 0x184 將被寫入PC中,這是loc_184標簽的地址。 每當a上加1,PC都會增加4,4也是ARM模式的指令長度,而且也是B指令的長度。這組里面有5個這樣的指令。 這5個B指令將傳遞控制流,也就是傳遞switch()中指定的字符串和對應的操作等等。 ### 11.2.3 ARM: 優化后的 Keil + thumb 模式 ``` #!bash 000000F6 EXPORT f2 000000F6 f2 000000F6 10 B5 PUSH {R4,LR} 000000F8 03 00 MOVS R3, R0 000000FA 06 F0 69 F8 BL __ARM_common_switch8_thumb ; switch 6 cases 000000FA ; ------------------------------------------------------------------------- 000000FE 05 DCB 5 000000FF 04 06 08 0A 0C 10 DCB 4, 6, 8, 0xA, 0xC, 0x10 ; jump table for switch statement 00000105 00 ALIGN 2 00000106 00000106 zero_case ; CODE XREF: f2+4 00000106 8D A0 ADR R0, aZero ; jumptable 000000FA case 0 00000108 06 E0 B loc_118 0000010A ; ------------------------------------------------------------------------- 0000010A 0000010A one_case ; CODE XREF: f2+4 0000010A 8E A0 ADR R0, aOne ; jumptable 000000FA case 1 0000010C 04 E0 B loc_118 0000010E ; ------------------------------------------------------------------------- 0000010E 0000010E two_case ; CODE XREF: f2+4 0000010E 8F A0 ADR R0, aTwo ; jumptable 000000FA case 2 00000110 02 E0 B loc_118 00000112 ; ------------------------------------------------------------------------- 00000112 00000112 three_case ; CODE XREF: f2+4 00000112 90 A0 ADR R0, aThree ; jumptable 000000FA case 3 00000114 00 E0 B loc_118 00000116 ; ------------------------------------------------------------------------- 00000116 00000116 four_case ; CODE XREF: f2+4 00000116 91 A0 ADR R0, aFour ; jumptable 000000FA case 4 00000118 00000118 loc_118 ; CODE XREF: f2+12 00000118 ; f2+16 00000118 06 F0 6A F8 BL __2printf 0000011C 10 BD POP {R4,PC} 0000011E ; ------------------------------------------------------------------------- 0000011E 0000011E default_case ; CODE XREF: f2+4 0000011E 82 A0 ADR R0, aSomethingUnkno ; jumptable 000000FA default case 00000120 FA E7 B loc_118 000061D0 EXPORT __ARM_common_switch8_thumb 000061D0 __ARM_common_switch8_thumb ; CODE XREF: example6_f2+4 000061D0 78 47 BX PC 000061D0 ; --------------------------------------------------------------------------- 000061D2 00 00 ALIGN 4 000061D2 ; End of function __ARM_common_switch8_thumb 000061D2 000061D4 CODE32 000061D4 000061D4 ; =============== S U B R O U T I N E ======================================= 000061D4 000061D4 000061D4 __32__ARM_common_switch8_thumb ; CODE XREF: __ARM_common_switch8_thumb 000061D4 01 C0 5E E5 LDRB R12, [LR,#-1] 000061D8 0C 00 53 E1 CMP R3, R12 000061DC 0C 30 DE 27 LDRCSB R3, [LR,R12] 000061E0 03 30 DE 37 LDRCCB R3, [LR,R3] 000061E4 83 C0 8E E0 ADD R12, LR, R3,LSL#1 000061E8 1C FF 2F E1 BX R12 000061E8 ; End of function __32__ARM_common_switch8_thumb ``` 一個不能確定的事實是thumb、thumb-2中的所有指令都有同樣的大小。甚至可以說是在這些模式下,指令的長度是可變的,就像x86一樣。 所以這一定有一個特別的表單,里面包含有多少個case(除了默認的case),然后和它們的偏移,并且給他們每個都加上一個標簽,這樣控制流就可以傳遞到正確的位置。 這里有一個特別的函數來處理表單和處理控制流,被命名為__ARM_common_switch8_thumb。它由“BX PC”指令開始,這個函數用來將處理器切換到ARM模式,然后你就可以看到處理表單的函數。不過對我們來說,在這里解釋它太復雜了,所以我們將省去一些細節。 但是有趣的是,這個函數使用LR寄存器作為表單的指針。還有,在這個函數調用后,LR將包含有緊跟著“BL __ARM_common_switch8_thumb”指令的地址,然后表單就由此開始。 當然,這里也不值得去把生成的代碼作為單獨的函數,然后再去重用它們。因此在switch()處理相似的位置、相似的case時編譯器并不會生成相同的代碼。 IDA成功的發覺到它是一個服務函數以及函數表,然后給各個標簽加上了合適的注釋,比如jumptable 000000FA case 0。
                  <ruby id="bdb3f"></ruby>

                  <p id="bdb3f"><cite id="bdb3f"></cite></p>

                    <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
                      <p id="bdb3f"><cite id="bdb3f"></cite></p>

                        <pre id="bdb3f"></pre>
                        <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

                        <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
                        <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

                        <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                              <ruby id="bdb3f"></ruby>

                              哎呀哎呀视频在线观看