你的浏览器版本过低,可能导致网站不能正常访问!
为了你能正常使用网站功能,请使用这些浏览器。

C语言——条件判断语句

[复制链接]
gaosmile 发布时间:2020-6-19 22:48

在C语言中,条件判断语句是程序的重要组成部分,也是系统业务逻辑的控制手段,教科书告诉我们switch...case...语句比if...else if...else执行效率要高,但这到底是为什么呢?本文尝试从汇编的角度予以分析并揭晓其中的奥秘。

switch...case与if...else的根本区别
switch...case会生成一个跳转表来指示实际的case分支的地址,而这个跳转表的索引号与switch变量的值是相等的。从而,switch...case不用像if...else那样遍历条件分支直到命中条件,而只需访问对应索引号的表项从而到达定位分支的目的。
具体地说,switch...case会生成一份大小(表项数)为最大case常量+1的跳表,程序首先判断switch变量是否大于最大case 常量,若大于,则跳到default分支处理;否则取得索引号为switch变量大小的跳表项的地址(即跳表的起始地址+表项大小*索引号),程序接着跳到此地址执行,到此完成了分支的跳转。
第一步,写一个demo程序:foo.c#include <stdio.h>
, L/ K) v# `) N3 k0 M8 p9 k* ~/ T1 _& e) V( t- _- V, d
static int
2 U2 r; i( X% g' Bfoo_ifelse(char c)+ i' f( a+ u3 \. v6 S) Z
{' u- }" }; r  L* c/ ^
        if (c == '0' || c == '1') {
; u( v5 r. F- i! R' X                c += 1;" L( |# c. e+ c8 j, b! J& _
        } else if (c == 'a' || c == 'b') {
3 R! J$ @! Q* N+ C# h                c += 2;# H, {6 f8 i9 X+ Y
        } else if (c == 'A' || c == 'B') {
6 B: u9 c8 @: {# `; k) @                c += 3;2 R2 \7 x* F1 r0 d* r/ F4 ^
        } else {
0 t: b5 b. ^+ Q4 `% g" }                c += 4;
2 j: T. H7 p1 O        }3 _4 x9 }( K/ W4 g' L3 U, v
3 k& {* C. |5 }5 J* Y
        return (c);2 P0 b1 F8 N, A# s
}
- T' H" U2 X7 d' G6 \9 j
" |# y/ f0 k( k" r. i% A4 Dstatic int4 g: z+ o1 e+ c2 K. }
foo_switch(char c)3 `% \4 @- R  ~8 h- H6 L
{
, i( g# E& t, F& ~( S, Q9 G        switch (c) {/ g5 d1 ~0 p# L" F8 B, K1 L$ |
                case '1':
: f  w( [5 {  g2 \7 t                case '0': c += 1; break;9 E- K+ r! Y8 s
                case 'b':1 @3 h5 A4 u3 K6 d
                case 'a': c += 2; break;
) Q: B( Y  t  n, Z- ~0 P. m6 Q                case 'B':
8 z7 M% ], J$ G6 F  m% H# o                case 'A': c += 3; break;
) i' h' T& i; U$ V6 U- i, X+ ^! g$ n                default:  c += 4; break;  c( B) V9 y- x' M0 _+ X
        }1 Q  |1 z5 b) z' V0 }: j
8 d8 j5 p% G  i0 R' [" h+ K- \
        return (c);
- f5 d, z" ^/ Z9 P6 S. B. Z}
$ p; j8 w; H! {& x6 Q0 X/ d4 X
1 B5 H8 B; `1 y  N4 m7 ^- l( q7 jint% J  u+ i# f3 \+ F, [! f; L2 M9 P" x
main(int argc, char **argv)9 h. N! Q6 g" E- y% x0 H
{
2 c2 m2 M7 p5 z  I# x, [/ c        int m1 = foo_ifelse('0');0 V' I* }: M0 N/ e8 N. B
        int m2 = foo_ifelse('1');
5 [; G/ R; p) L        int n1 = foo_switch('a');: q" a5 S) x4 ^3 U6 k! l# X
        int n2 = foo_switch('b');, H1 k6 P! ^1 r. R
        (void) printf("%c %c %c %c\n", m1, m2, n1, n2);
# W1 I/ h. z8 y! {        return (0);
+ }/ ]- k7 Q8 t}

5 Z5 R; [' O) S8 O; W( C' _第二步,在Ubuntu上使用gcc编译
$ gcc -g -o foo foo.c
第三步,使用gdb对二进制文件foo反汇编 (使用intel语法)o 反汇编foo_ifelse()
) @; ?% D( P' T8 a(gdb) set disassembly-flavor intel) h9 i/ k. w- L1 n) K( {6 D
(gdb) disas /m foo_ifelse
: P* O' C  d  z- l( k9 a# eDump of assembler code for function foo_ifelse:% E$ f: c0 x9 k$ f* }
4       {4 y9 ]3 z3 J5 }& V# x
   0x0804841d <+0>:     push   ebp& L+ g  U9 M  m
   0x0804841e <+1>:     mov    ebp,esp( a: p8 H: b/ G4 q
   0x08048420 <+3>:     sub    esp,0x4- r& i8 v* p  ^( `
   0x08048423 <+6>:     mov    eax,DWORD PTR [ebp+0x8]
8 o: J/ F, k! Q  |/ B- o- g   0x08048426 <+9>:     mov    BYTE PTR [ebp-0x4],al' g- ?# m9 }2 z. ]

6 ~/ U0 f( `9 [- k" E5               if (c == '0' || c == '1') {  z! T! x8 ^2 V: p& j# N; E( |
   0x08048429 <+12>:    cmp    BYTE PTR [ebp-0x4],0x30+ b! c# o; A  c* o
   0x0804842d <+16>:    je     0x8048435 <foo_ifelse+24>
+ V2 ^! X+ N3 w2 I* o   0x0804842f <+18>:    cmp    BYTE PTR [ebp-0x4],0x31. e8 P6 a3 r( b9 \+ ?) D
   0x08048433 <+22>:    jne    0x8048441 <foo_ifelse+36>
' ?% H) g+ i  k9 b% M+ y
; ^7 Y0 X9 M+ E: L( [6                       c += 1;& v6 ~" f/ s. N5 r& ]/ J
   0x08048435 <+24>:    movzx  eax,BYTE PTR [ebp-0x4]5 x' G( w3 r  x/ w5 y
   0x08048439 <+28>:    add    eax,0x11 U! L% ?  x+ F: q  D7 j4 K
   0x0804843c <+31>:    mov    BYTE PTR [ebp-0x4],al1 P7 w% g3 G/ q
   0x0804843f <+34>:    jmp    0x804847b <foo_ifelse+94>) [  U8 }- y$ l% s5 G
2 u* K- p& n/ ?6 D* V- G
7               } else if (c == 'a' || c == 'b') {
; t3 K2 t" [; C" K+ c0 U   0x08048441 <+36>:    cmp    BYTE PTR [ebp-0x4],0x61
2 e% D$ u& Z3 Y   0x08048445 <+40>:    je     0x804844d <foo_ifelse+48>7 X0 M3 U6 b. |' P7 a  Y1 Z* m
   0x08048447 <+42>:    cmp    BYTE PTR [ebp-0x4],0x629 N' }! u# W- B% I5 R+ x
   0x0804844b <+46>:    jne    0x8048459 <foo_ifelse+60>6 \6 }1 f, a( x5 M; S, a
4 _+ R$ G! u! x- M$ c
8                       c += 2;
; O. k) ]3 s: W   0x0804844d <+48>:    movzx  eax,BYTE PTR [ebp-0x4]
, J2 S( M4 S4 @7 ^! q8 a   0x08048451 <+52>:    add    eax,0x2
# d3 U' _2 k. Y; x   0x08048454 <+55>:    mov    BYTE PTR [ebp-0x4],al% g1 h8 s" G. S. U. K
   0x08048457 <+58>:    jmp    0x804847b <foo_ifelse+94>) b& D' V7 R; p/ X5 t+ l: [; s

6 l7 _  T% z* ?1 N7 \( v" O+ D9               } else if (c == 'A' || c == 'B') {5 w' J6 `/ p' r5 g5 [3 p$ K* L; T0 Q
   0x08048459 <+60>:    cmp    BYTE PTR [ebp-0x4],0x416 W9 y; ]0 c- _7 r
   0x0804845d <+64>:    je     0x8048465 <foo_ifelse+72>
. Y# r+ a; g, b+ F2 f& b; I0 Z) {   0x0804845f <+66>:    cmp    BYTE PTR [ebp-0x4],0x42; d8 b' C2 y: k- r- e* L- |
   0x08048463 <+70>:    jne    0x8048471 <foo_ifelse+84>
1 X3 P6 w% V9 t9 W- J! F. U* P6 b" a6 _# P2 Y3 Q7 h' k+ b
10                      c += 3;
0 G/ t! {. b5 G! S7 n6 J2 ~   0x08048465 <+72>:    movzx  eax,BYTE PTR [ebp-0x4]
* ]" Z  w, A. w0 X   0x08048469 <+76>:    add    eax,0x36 n  w) E* |9 `, T; g! M+ I/ g
   0x0804846c <+79>:    mov    BYTE PTR [ebp-0x4],al
; @+ Y6 [, Y( Q! p6 X2 H6 A   0x0804846f <+82>:    jmp    0x804847b <foo_ifelse+94># c- }+ q- E  M' L3 @+ u
6 @! n# C2 T) P* q) ], W! y1 ~
11              } else {$ V1 v' v' K) L: L
12                      c += 4;7 q/ ?/ U  J7 ^
   0x08048471 <+84>:    movzx  eax,BYTE PTR [ebp-0x4]' G  }. L' q& v! G
   0x08048475 <+88>:    add    eax,0x46 y( Y- s6 s% M, {
   0x08048478 <+91>:    mov    BYTE PTR [ebp-0x4],al5 L/ R& i/ X8 w1 J# [1 Y. g
1 d3 w/ p' F& b7 q* ^0 i
13              }
8 {6 m- a& R. V- D* D7 G14
7 {- \: Z# e  a6 g7 ?( A9 K15              return (c);. T# l1 B* |6 L9 E% U9 f; u
   0x0804847b <+94>:    movsx  eax,BYTE PTR [ebp-0x4]
; Q# Z' Y$ [# R3 P2 y" J3 j% M& u6 F+ I% z, j$ B
16      }
: L$ D/ D( d7 K4 H* Y  o   0x0804847f <+98>:    leave$ K$ @" R& p3 `6 Y3 T
   0x08048480 <+99>:    ret
1 T* J& o# x. n6 w# N
9 H; J" K: f. @1 t  r# KEnd of assembler dump.
  ~" Q1 A. _+ p0 m' A(gdb)o 反汇编foo_ifelse()* ^6 c( q( Z  l5 |
(gdb) set disassembly-flavor intel9 K# c3 u5 h) m% W
(gdb) disas /m foo_ifelse
5 p8 V- x, m1 ^' _2 U' lDump of assembler code for function foo_ifelse:$ c$ d6 F+ m% J; x7 h
4       {
0 q1 a; Y3 M* C: n   0x0804841d <+0>:     push   ebp7 U( x( L4 L& f% W" F+ K1 j% [
   0x0804841e <+1>:     mov    ebp,esp) d5 ^$ E  c; r9 k, F
   0x08048420 <+3>:     sub    esp,0x4
& D9 ^, q+ X7 _   0x08048423 <+6>:     mov    eax,DWORD PTR [ebp+0x8]
7 h6 c) Z$ _& {3 E' `) k5 ~( R   0x08048426 <+9>:     mov    BYTE PTR [ebp-0x4],al' B' N/ t9 X5 Q+ x  t

* L1 }. I! q5 @6 G& O5 A: {* r5               if (c == '0' || c == '1') {# ?- g+ S9 P! j1 |" N
   0x08048429 <+12>:    cmp    BYTE PTR [ebp-0x4],0x304 E, y8 u( A8 p, Z8 E$ V/ x
   0x0804842d <+16>:    je     0x8048435 <foo_ifelse+24>
* p! L8 U' r: H/ A! q9 f   0x0804842f <+18>:    cmp    BYTE PTR [ebp-0x4],0x31
4 \- }3 q4 P! \   0x08048433 <+22>:    jne    0x8048441 <foo_ifelse+36>$ Q# `6 X9 k) p8 \5 `

4 n, \! @: g$ R6 i6                       c += 1;7 L+ B' F$ U( ?/ K
   0x08048435 <+24>:    movzx  eax,BYTE PTR [ebp-0x4]' u5 V# X& i* v
   0x08048439 <+28>:    add    eax,0x1
( m! v/ n! Y0 Z3 f7 u   0x0804843c <+31>:    mov    BYTE PTR [ebp-0x4],al
6 _% [7 i; i' t7 @3 U   0x0804843f <+34>:    jmp    0x804847b <foo_ifelse+94>' J1 X- J$ _0 l9 q: E; L

$ h& y" u) W7 E+ t5 v7               } else if (c == 'a' || c == 'b') {  W, w) Z2 \) B; E% V  S5 ^
   0x08048441 <+36>:    cmp    BYTE PTR [ebp-0x4],0x61
% s9 Q* S# U' S* }% c1 y. o   0x08048445 <+40>:    je     0x804844d <foo_ifelse+48>/ D" [/ l, ?7 y9 M5 y
   0x08048447 <+42>:    cmp    BYTE PTR [ebp-0x4],0x62  V4 g$ O1 ]  I8 p! K1 k/ @( H
   0x0804844b <+46>:    jne    0x8048459 <foo_ifelse+60>/ z2 X% t5 x  |% I4 d! m, W
/ U! V& S5 D$ ~3 E/ N
8                       c += 2;
3 A6 E& V: _2 ]! P( u  j' R   0x0804844d <+48>:    movzx  eax,BYTE PTR [ebp-0x4]
! o+ Y3 r* W& r   0x08048451 <+52>:    add    eax,0x2
  i* q5 U# x2 P# ?+ G   0x08048454 <+55>:    mov    BYTE PTR [ebp-0x4],al
( D/ T) x( I6 h; c: Y9 N, J   0x08048457 <+58>:    jmp    0x804847b <foo_ifelse+94>3 c) m/ w$ C/ Q8 N. a
5 J1 j2 \! B. u$ Y! |
9               } else if (c == 'A' || c == 'B') {5 a. w, V/ z7 _  k8 c/ f+ X% _5 Y# I
   0x08048459 <+60>:    cmp    BYTE PTR [ebp-0x4],0x41
, v/ F, X' N& P" Y6 E2 y   0x0804845d <+64>:    je     0x8048465 <foo_ifelse+72>& z# R4 G4 |9 J  i3 e, j% K
   0x0804845f <+66>:    cmp    BYTE PTR [ebp-0x4],0x42# Y& ], ?6 o: y4 ^3 w
   0x08048463 <+70>:    jne    0x8048471 <foo_ifelse+84>
- W, ~# j6 ?8 ?% C' q, `% @
% E% C6 L6 r0 I, a7 s; {1 A* \0 O10                      c += 3;+ R, j2 S; z& t% ^5 U9 v
   0x08048465 <+72>:    movzx  eax,BYTE PTR [ebp-0x4]
$ _: {5 w" R3 ~& a   0x08048469 <+76>:    add    eax,0x3
7 _1 L; v! n% a) f3 M- [: \   0x0804846c <+79>:    mov    BYTE PTR [ebp-0x4],al1 w2 m/ s: S5 c; V
   0x0804846f <+82>:    jmp    0x804847b <foo_ifelse+94>9 D' s2 I- i# b6 v: ^5 C
  l% n( u# \  g& G& K' i+ ]" L. J
11              } else {( r1 o2 b( T$ m: ]
12                      c += 4;
" n' L8 d- o* U% V" i   0x08048471 <+84>:    movzx  eax,BYTE PTR [ebp-0x4]
/ @- \: z4 h: Z, S" w3 r8 V. W0 i   0x08048475 <+88>:    add    eax,0x4
% Y) c5 j7 A' m( p   0x08048478 <+91>:    mov    BYTE PTR [ebp-0x4],al
5 ?6 ?+ `1 G  [- H2 P4 X; t+ O1 M0 d
13              }
2 m* Y6 ^3 i- B$ ^3 P. w8 s14
' s2 B' b) u* [5 H  P+ r: u  P$ A# m& c15              return (c);
6 i/ f9 @8 B3 G7 Z' m   0x0804847b <+94>:    movsx  eax,BYTE PTR [ebp-0x4]
* A8 J8 A' k/ a* X5 _: j- h
1 U- M) R) K4 \) u3 ?+ W16      }7 s- F, {0 Q) E6 {! ^0 Y8 B
   0x0804847f <+98>:    leave- v' Y2 K. N$ Z% C  J
   0x08048480 <+99>:    ret
5 p; ]: k4 A8 X, M' g  w9 `' i+ k  v# P4 b9 ^" u# _1 F5 k3 c/ J# ?
End of assembler dump.7 j6 O! E+ B: N* A$ J/ b" }+ t
(gdb)

+ J- W) T& [8 M! ^4 a. F5 ^3 l
o 反汇编foo_switch()
(gdb) set disassembly-flavor intel. z; |, [9 G( P2 k& M
(gdb) disas /m foo_switch
3 |) T" G) T6 b1 S6 W  ?8 _( A% CDump of assembler code for function foo_switch:1 e) d6 ?% C$ Z( N% w7 A
20      {7 l% Y. U7 _" [; }# [% r
   0x08048481 <+0>:     push   ebp
1 O  Z- s  K- s( P7 }; S   0x08048482 <+1>:     mov    ebp,esp( W, u9 t9 |9 f8 f2 o& Y' _* m) u. e
   0x08048484 <+3>:     sub    esp,0x4
4 ~, t, g3 }- B. ^* F. B) ~2 D   0x08048487 <+6>:     mov    eax,DWORD PTR [ebp+0x8]# I9 S) T7 {) v$ \* i
   0x0804848a <+9>:     mov    BYTE PTR [ebp-0x4],al' m3 \" @3 `1 h2 j+ {, X

5 v" {' R: C9 x9 M1 |- ]21              switch (c) {1 u: l/ b6 _- \
   0x0804848d <+12>:    movsx  eax,BYTE PTR [ebp-0x4]
! _" Q. {4 y# G7 N( X3 R   0x08048491 <+16>:    sub    eax,0x308 r  Z" g% ?( r1 p& A" t
   0x08048494 <+19>:    cmp    eax,0x32  X; \, Q! |3 q6 s) K9 Y7 t. `8 `
   0x08048497 <+22>:    ja     0x80484c6 <foo_switch+69>! s0 A# h+ ^$ I! U. [" Z
   0x08048499 <+24>:    mov    eax,DWORD PTR [eax*4+0x80485f0]
1 d) ]' L" A) F% J. ]$ A( l   0x080484a0 <+31>:    jmp    eax" J' E! r3 s6 O+ ^: `+ v8 [' u- w

$ l% L5 [8 T; l* R0 E$ J22                      case '1':9 Q) u+ @" h% b, y7 |
23                      case '0': c += 1; break;5 I" u, h7 U. |, {" Y) m
   0x080484a2 <+33>:    movzx  eax,BYTE PTR [ebp-0x4]
- T! M( S* G" b7 c3 K   0x080484a6 <+37>:    add    eax,0x1' E; X6 y5 u2 |  Y
   0x080484a9 <+40>:    mov    BYTE PTR [ebp-0x4],al
; S4 ]4 r/ n6 _2 c8 Y   0x080484ac <+43>:    jmp    0x80484d1 <foo_switch+80>7 u. J) d% Q& w# g- N' O: `, f2 `
$ W% U) I3 }( o: Z9 O; _* O
24                      case 'b':! ], ~. b1 x  s  G; ^
25                      case 'a': c += 2; break;$ `, i0 W' O) h  P1 X6 o
   0x080484ae <+45>:    movzx  eax,BYTE PTR [ebp-0x4]
: H' I  D8 J: L( @6 R1 b6 E  i8 T   0x080484b2 <+49>:    add    eax,0x2) _8 W  |$ S" i" v' H7 U1 Q+ w
   0x080484b5 <+52>:    mov    BYTE PTR [ebp-0x4],al
7 F$ W# M0 r3 b9 R9 n   0x080484b8 <+55>:    jmp    0x80484d1 <foo_switch+80>
! ?* i5 ?" h; L" N9 Y
$ ?0 P/ E* A& Z$ ], b26                      case 'B':- U& @# b2 T  j) n& h% |
27                      case 'A': c += 3; break;: U5 I  a7 T0 U
   0x080484ba <+57>:    movzx  eax,BYTE PTR [ebp-0x4]
" o" V1 z! V. M( T- d) V   0x080484be <+61>:    add    eax,0x3
% q$ D+ v: Y  X: @   0x080484c1 <+64>:    mov    BYTE PTR [ebp-0x4],al
' f4 c6 L! ?8 d: D0 A   0x080484c4 <+67>:    jmp    0x80484d1 <foo_switch+80>0 a! ]8 ^1 F) z+ ^8 Q4 r) Y
& L+ w9 V) r4 z) @, z, W- J
28                      default:  c += 4; break;" d2 z+ J0 J# x0 S3 W
   0x080484c6 <+69>:    movzx  eax,BYTE PTR [ebp-0x4]
- r; [) a+ y( t- U   0x080484ca <+73>:    add    eax,0x4
, P, D. H/ K0 T4 ^" O* S/ t/ X   0x080484cd <+76>:    mov    BYTE PTR [ebp-0x4],al, I( l8 y8 _0 c5 s4 a, Z9 z
   0x080484d0 <+79>:    nop
5 b1 j6 I* u, h- D3 m) v. w8 C6 W) Z+ p1 C7 V, o. b. f+ a
29              }- F; w: p6 c3 e' T  D
30; s! h/ L2 H8 X( F$ M9 }
31              return (c);
) K& k- R9 k. Q+ n$ D/ {   0x080484d1 <+80>:    movsx  eax,BYTE PTR [ebp-0x4]
* u! E2 g1 ]7 }3 C0 k8 H6 p" y- g1 P8 _4 X7 G' ]) m2 o+ y7 ~" h. D6 G
32      }- e" j8 ~0 e7 }6 y
   0x080484d5 <+84>:    leave/ J- T1 X" c5 n% R; ?
   0x080484d6 <+85>:    ret7 t+ B9 j" f6 L' J# y3 H7 i
9 t  A2 U: G* Y6 A+ a1 Y
End of assembler dump.2 F. g3 ]$ S  R
(gdb)

& B% F+ F3 K" c2 F' c8 t分析:
  • 在foo_ifelse()中,采用的方法是按顺序比较,如满足条件,则执行对应的代码,否则跳转到下一个分支再进行比较;
  • 在foo_switch()中,下面的这段汇编代码比较有意思,

    $ E$ o4 F1 [# i$ F7 }
..
) ]  ^- [1 S8 r21 switch (c) {
- f; J: R' o; i: P5 K8 m   0x0804848d <+12>:    movsx  eax,BYTE PTR [ebp-0x4]( O5 c% J% S" `: V0 g
   0x08048491 <+16>:    sub    eax,0x30) V% G4 {; P5 z
   0x08048494 <+19>:    cmp    eax,0x32# Y6 T& [6 \$ Z3 W
   0x08048497 <+22>:    ja     0x80484c6 <foo_switch+69># S+ L* N9 b, F) [
   0x08048499 <+24>:    mov    eax,DWORD PTR [eax*4+0x80485f0]6 n: T: y) W& A  ]; |; m* D
   0x080484a0 <+31>:    jmp    eax
. x  ?7 K5 Z, h7 Z3 B, S; J$ k..

; l0 n, f1 S/ _1 Z9 Q
注意:
第17行 jmp eax
也就是说,当c的取值不同,是什么机制保证第17行能跳转到正确的位置开始执行呢?
第16行: eax = [eax * 4 + 0x80485f0]
搞清楚了从地址0x80485f0开始,对应的内存里面的内容也就回答了刚才的问题。
执行完第16行后,
  • 当c为'1'或'0'时, eax的值应该是0x080484a2;
  • 当c为'b'或'a'时, eax的值应该是0x080484ae;
  • 当c为'B'或'A'时, eax的值应该是0x080484ba;
    6 x3 [; D; |8 B$ `; o* \
通过gdb查看对应的内存,确实如此!
>>> ord('1') - 0x30
5 Q* f, q7 g, h% B9 F$ p+ O  k& u" c>>> ord('0') - 0x30
- S6 X) k+ t9 O- b) U. ]; s" {5 h(gdb) x /2wx  0*4+0x80485f0+ D7 X4 I8 b9 k: B7 U0 U" z
0x80485f0:    0x080484a2    0x080484a2; M& x& e/ F. B1 R7 |! q7 d
+ i+ r, ~$ V  ?9 V. [& x- @
>>> ord('b') - 0x30
: E1 y7 g7 O3 K2 d3 a>>> ord('a') - 0x30
9 G: J5 Q1 [8 i/ W& \(gdb) x /2wx 49*4+0x80485f0
3 R! A  ~9 l$ Z+ U. r" D! ]/ j) G0x80486b4:    0x080484ae    0x080484ae9 M9 v9 H7 F3 `# R7 R
                4 W2 \2 W$ x! W" }' z
>>> ord('B') - 0x30# P( |+ H) G+ _- _
>>> ord('A') - 0x30
' Y/ w- r, A( u5 {  F6 }(gdb) x /2wx 17*4+0x80485f0$ V$ m% d+ m' j5 D
0x8048634:    0x080484ba    0x080484ba
( Q: [+ d% |# }
那么,我们可以大胆的猜测,虽然c的取值不同但是跳转的IP确实是精准无误的,一定是编译阶段就被设定好了,果真如此吗?接下来分析一下对应的二进制文件foo,
第四步,使用objdump查看foo,$ objdump -D foo > /tmp/x9 e1 E3 W. u' d# ^" B1 Z) J

7 P( K7 ~0 G3 v' q$ ]" R9 F$ vim /tmp/x
3 ~# A& P, {! R/ h 509 Disassembly of section .rodata:
, Y' e7 R3 E0 ^8 \ ...
! w+ @  M: z$ L, X- X  n8 W0 g 518  80485f0:       a2 84 04 08 a2          mov    %al,0xa2080484
5 z2 a+ _+ O- }) r 519  80485f5:       84 04 08                test   %al,(%eax,%ecx,1)( m% v, h% q5 T. v5 t
...
& {: X7 E( L/ G- Z+ j( ~/ t 534  8048630:       c6 84 04 08 ba 84 04    movb   $0x8,0x484ba08(%esp,%eax,1)
2 |" a. i& D8 N. }& q 535  8048637:       089 N' T' @, Y- q  X& i
536  8048638:       ba 84 04 08 c6          mov    $0xc6080484,%edx5 K( Q+ b/ Z( F% Q- {
...
. Q0 t( L3 X$ z5 L. e& _% a 566  80486b0:       c6 84 04 08 ae 84 04    movb   $0x8,0x484ae08(%esp,%eax,1)0 |( |# q3 k$ n2 {" B
567  80486b7:       08' v& b( {& T7 Y: H( d1 W
568  80486b8:       ae                      scas   %es%edi),%al
2 ?) @* |; ?) q3 u 569  80486b9:       84 04 08                test   %al,(%eax,%ecx,1)
( I8 @% P7 ]; K7 b% K0 F ...
7 @9 S# _! s) U/ O
在0x80485f0地址,存的8个字节正好是0x080484a2, 0x080484a2 (注意:按照小端的方式阅读)
在0x80486b4地址,存的8个字节正好是0x080484ae, 0x080484ae
在0x8048634地址,存的8个字节正好是0x080484ba,0x080484ba
果然不出所料,要跳转的IP的值正是在编译的时候存入了.rodata(只读数据区)。一旦foo开始运行,对应的内存地址就填写上了正确的待跳转地址,接下来只不过是根据c的取值计算出对应的IP存放的内存起始地址X,从X中取出待跳转的地址,直接跳转就好。
16    0x08048499 <+24>:    mov    eax,DWORD PTR [eax*4+0x80485f0]0 Y. C% M7 j  s$ k2 @( L+ K0 ]
17    0x080484a0 <+31>:    jmp    eax
! l% W) D) Y" p: u, y
到此为止,我们已经搞清楚了为什么switch...case...语句相对于if...else if...else...来说执行效率要高的根本原因。简言之,编译的时候创建了一个map存于.rodata区中,运行的时候直接根据输入(c的值)查表,找到对应的IP后直接跳转。(省去了cmp, jmp -> cmp, jmp -> cmp, jmp...这一冗长的计算过程。)
总结:
switch...case...执行效率高,属于典型的以空间换时间。也就是说,(套用算法的行话)以提高空间复杂度为代价降低了时间复杂度。

2 h' C6 ~3 O8 k6 `
收藏 评论1 发布时间:2020-6-19 22:48

举报

1个回答
chat2019 回答时间:2020-6-20 12:23:11
赞一个

所属标签

关于
我们是谁
投资者关系
意法半导体可持续发展举措
创新与技术
意法半导体官网
联系我们
联系ST分支机构
寻找销售人员和分销渠道
社区
媒体中心
活动与培训
隐私策略
隐私策略
Cookies管理
行使您的权利
官方最新发布
人形机器人运动控制、感知与智能配电
半导体创新技术与应用方向
EE架构与软件定义汽车
12V/48V 汽车智能配电(SPD)
区域控制单元(ZCU)与分区架构
关注我们
st-img 微信公众号
st-img 手机版