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

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>3 M) i; ~$ y5 G/ o, ?+ k# d0 A
; c$ z9 ]1 w1 G' O6 z' y
static int
- `5 |, X4 G) W7 B+ Vfoo_ifelse(char c)" {- g- |# d1 c$ q
{
- w* g& d6 ^: j- R9 f4 z& ^        if (c == '0' || c == '1') {
4 U$ r' l* }9 l, V& k; ~; D1 E                c += 1;
6 Q7 B+ L- w: j& G/ s$ P4 w- }        } else if (c == 'a' || c == 'b') {6 y9 E2 k" j* [& n( Q
                c += 2;
" ~& r& a4 n) G% s2 Y, M& C5 ~* ?  x        } else if (c == 'A' || c == 'B') {
- ~/ G" q: S$ A" h" V* L  K                c += 3;
! Y$ N$ {8 E& U( R; [9 r* q        } else {0 K. f$ J% d7 L% x" {+ P$ \3 O) l
                c += 4;+ I" S, K% J: }; ?3 G
        }
4 b) V6 ?( t( p: V, m3 g- I4 |* [9 O! `' b
        return (c);
$ D( `* \$ G) X! x}
! ~/ `5 e3 r6 n; F- n7 c# Y
1 P9 B7 o* T+ M* k: Qstatic int* Q. A$ h4 V/ m$ b5 ^7 ~, }
foo_switch(char c)+ A* m- b; ]! l
{
; p- U4 D, N  t: s, S4 x; D* [        switch (c) {
0 }! t: D+ o& Q9 \; N                case '1':
, h3 l: k4 T1 ?( ^9 N* P                case '0': c += 1; break;
. b9 D5 Y' K' v9 `* Y                case 'b':: n  V- u3 k# ^
                case 'a': c += 2; break;
/ F) z  F+ {3 b0 \+ [                case 'B':2 y* a2 ?& }  m5 R) @
                case 'A': c += 3; break;
9 E: k8 \5 A) M& `& X1 R                default:  c += 4; break;
0 q- F" D+ T4 F6 P  s# w; ~# a2 w, R0 L. f        }9 F& g9 O9 Y8 d) L# R- q+ W
9 l# s5 ~' O! g0 m1 i6 \# C
        return (c);
- S, m; t, s: p% ^# g}& K5 j; @! N7 V
. [# @  E/ j: u
int4 {/ x5 Q3 P* V8 S, |+ R: v& O
main(int argc, char **argv)- Q0 n# w. D$ i  H- x" w
{" d: C8 X& y& E% O9 c, A/ A, I
        int m1 = foo_ifelse('0');2 ~7 e% @; D2 h) E% \
        int m2 = foo_ifelse('1');
) D: m5 `$ r3 v6 u; ]        int n1 = foo_switch('a');
2 [- U' [! W7 i; E! A6 ~        int n2 = foo_switch('b');  ~3 `& Z7 z3 i" e4 O; O, Y/ u8 w
        (void) printf("%c %c %c %c\n", m1, m2, n1, n2);
/ Z1 s& L! |7 q. B% V        return (0);
2 a  m* y. c% j6 [4 M4 s7 {2 ]}

. c! t7 D  P2 q- Y% s1 ?6 K, `第二步,在Ubuntu上使用gcc编译
$ gcc -g -o foo foo.c
第三步,使用gdb对二进制文件foo反汇编 (使用intel语法)o 反汇编foo_ifelse()# b9 b8 Y) g' L( I
(gdb) set disassembly-flavor intel" R' t$ O& t2 i. n& b2 b
(gdb) disas /m foo_ifelse
( h1 V" T6 u' y3 ~" d+ ?Dump of assembler code for function foo_ifelse:1 Z4 T6 h9 q  f! V* R* R! y
4       {% H0 |: V8 }1 Z1 H% F8 Z/ F
   0x0804841d <+0>:     push   ebp
3 i- \2 b; {: D' y   0x0804841e <+1>:     mov    ebp,esp
. N9 f- r! @: ^0 B, k" a; D* t& T   0x08048420 <+3>:     sub    esp,0x47 S  i8 p( i/ z8 ?/ G- G! h
   0x08048423 <+6>:     mov    eax,DWORD PTR [ebp+0x8]4 M! h! R7 J) p: Q5 k; ~
   0x08048426 <+9>:     mov    BYTE PTR [ebp-0x4],al4 _* q% w# Y4 d  f0 g+ b* M7 s7 E. L

' v& f* c# T4 X5               if (c == '0' || c == '1') {8 f2 S3 P- B. }/ Y4 U
   0x08048429 <+12>:    cmp    BYTE PTR [ebp-0x4],0x30
) F+ {7 n4 D0 S   0x0804842d <+16>:    je     0x8048435 <foo_ifelse+24>
8 p% l9 k( H$ V$ y, D   0x0804842f <+18>:    cmp    BYTE PTR [ebp-0x4],0x319 n% t* M, A; ?: q, ~( s
   0x08048433 <+22>:    jne    0x8048441 <foo_ifelse+36>
' e6 m$ t# c: `, b9 D5 y2 R; \( S
6                       c += 1;
$ k/ Y$ i' Y1 T   0x08048435 <+24>:    movzx  eax,BYTE PTR [ebp-0x4]9 w  }; Y7 V! ^$ T8 P/ w
   0x08048439 <+28>:    add    eax,0x1
9 F  S0 P/ c9 k0 j5 c& `  W2 }; j* \   0x0804843c <+31>:    mov    BYTE PTR [ebp-0x4],al
9 Z0 C% _6 M5 e* u& p0 j, T   0x0804843f <+34>:    jmp    0x804847b <foo_ifelse+94>6 Z/ p1 c- q+ b

+ x. z  X0 a; T7               } else if (c == 'a' || c == 'b') {7 ^- S; O8 f' D/ ]8 r7 r
   0x08048441 <+36>:    cmp    BYTE PTR [ebp-0x4],0x61
( A% M5 V" @" G4 Y* ~   0x08048445 <+40>:    je     0x804844d <foo_ifelse+48>5 h1 `% P; u$ q4 M
   0x08048447 <+42>:    cmp    BYTE PTR [ebp-0x4],0x629 [( V# }. @& R* S) c! b
   0x0804844b <+46>:    jne    0x8048459 <foo_ifelse+60>
* A# z9 c- e" O+ k, {% h9 W
: A1 v" s5 d7 M$ F& T/ t8                       c += 2;# l% \; b8 T" s! g1 W4 e+ u  k
   0x0804844d <+48>:    movzx  eax,BYTE PTR [ebp-0x4]
' |! W6 w4 i' ]4 m) s6 L   0x08048451 <+52>:    add    eax,0x2
; F1 K+ G& E2 D0 U6 s0 p   0x08048454 <+55>:    mov    BYTE PTR [ebp-0x4],al0 o1 M. f' E- ?5 o0 f9 x
   0x08048457 <+58>:    jmp    0x804847b <foo_ifelse+94>3 U' c+ b, g# ~5 L' z
7 `6 Z) A" ^% @( i% R# a( U0 h
9               } else if (c == 'A' || c == 'B') {1 {% Z% D  L, R5 M
   0x08048459 <+60>:    cmp    BYTE PTR [ebp-0x4],0x41+ x2 U! `, O; g. [1 i
   0x0804845d <+64>:    je     0x8048465 <foo_ifelse+72>3 f+ W8 N4 Z2 @# \
   0x0804845f <+66>:    cmp    BYTE PTR [ebp-0x4],0x42
! m! D1 X+ {. ^   0x08048463 <+70>:    jne    0x8048471 <foo_ifelse+84>
9 A, Z0 C; T; t% S/ |$ ~0 D* ~, B* l# L
10                      c += 3;
: d8 A6 P5 w- i   0x08048465 <+72>:    movzx  eax,BYTE PTR [ebp-0x4]
& P5 l; |2 `8 {3 _   0x08048469 <+76>:    add    eax,0x3
; @+ ~8 H( ?: k( p4 `   0x0804846c <+79>:    mov    BYTE PTR [ebp-0x4],al
" B  `- s5 E" m( k   0x0804846f <+82>:    jmp    0x804847b <foo_ifelse+94>
2 ?( a" G- \( m8 a( J
  r% W7 d9 J0 Y8 T11              } else {
; e& ?0 u+ V$ i- d0 k12                      c += 4;3 g  a; z7 ^0 ?. N: n
   0x08048471 <+84>:    movzx  eax,BYTE PTR [ebp-0x4]0 F* w3 H, Q5 F8 k/ a1 k+ K
   0x08048475 <+88>:    add    eax,0x4
) F+ ?4 |' n; G( O, ?8 F   0x08048478 <+91>:    mov    BYTE PTR [ebp-0x4],al
% n) p5 d* r' `7 D. g/ i: g5 e- u; }3 G& u0 E
13              }/ K. d6 h" ^: g: _" R1 P( a% F% q
14
6 Q$ Y! Q- t7 s4 U8 A# L15              return (c);3 |( {* P5 R' |3 j, |6 N
   0x0804847b <+94>:    movsx  eax,BYTE PTR [ebp-0x4]3 V; Q* s+ ~6 m  W

' ?4 f3 q) w- r16      }
7 }- N" s/ @( s# O6 t   0x0804847f <+98>:    leave
( U8 I  F" |, e4 G   0x08048480 <+99>:    ret/ V( [' K+ s1 T1 t, O2 s; W9 V
' ~; Q  J: w+ m1 v" P: B, M
End of assembler dump.3 W+ S; h) L2 |4 n/ ?
(gdb)o 反汇编foo_ifelse()
# u7 w6 X) _- P, L: S; F3 \6 B(gdb) set disassembly-flavor intel
; |' I# r2 o3 x; J, A, o. |(gdb) disas /m foo_ifelse
- x" ?% J1 a) K. z) }Dump of assembler code for function foo_ifelse:, ]# D3 H- u& a9 V
4       {+ R0 Q/ u* l) q* w
   0x0804841d <+0>:     push   ebp$ y; o1 ?4 K; Z
   0x0804841e <+1>:     mov    ebp,esp- R/ g8 V% Z& I5 f: ~; r# S
   0x08048420 <+3>:     sub    esp,0x44 w: d# g. u/ ~/ J% ]
   0x08048423 <+6>:     mov    eax,DWORD PTR [ebp+0x8]6 k, w5 x" I: r) N( H. p$ j
   0x08048426 <+9>:     mov    BYTE PTR [ebp-0x4],al* {7 g: f+ l# d7 q" r0 d% U
) j$ s7 P6 S; n0 ^, S9 G5 Y, L2 L8 {
5               if (c == '0' || c == '1') {
' w, k: W* Q# g( }1 Q' y6 L   0x08048429 <+12>:    cmp    BYTE PTR [ebp-0x4],0x307 a6 @+ Y% k; c. g& ?
   0x0804842d <+16>:    je     0x8048435 <foo_ifelse+24>+ i# H  G9 l5 |6 S/ E/ _: q
   0x0804842f <+18>:    cmp    BYTE PTR [ebp-0x4],0x31: [. {7 w( o$ d) x; f. b
   0x08048433 <+22>:    jne    0x8048441 <foo_ifelse+36>+ E$ z+ \* p8 L* g

: E' U* `" t5 y# ?  |, P6                       c += 1;- [7 C2 y9 c3 c5 E! C1 C/ o7 S
   0x08048435 <+24>:    movzx  eax,BYTE PTR [ebp-0x4]
& ]) I8 E2 h3 z& h/ d8 M   0x08048439 <+28>:    add    eax,0x1
# O" e- ~" |; J) @   0x0804843c <+31>:    mov    BYTE PTR [ebp-0x4],al( y# s$ S1 a3 S3 x. B* ]
   0x0804843f <+34>:    jmp    0x804847b <foo_ifelse+94>6 H; c6 k9 C  l8 b5 q! L- S' q

3 p* t$ y4 V. l  [' W$ b" A7               } else if (c == 'a' || c == 'b') {+ |* T2 }' j$ Z5 f  U
   0x08048441 <+36>:    cmp    BYTE PTR [ebp-0x4],0x61
$ g* n) y* x$ n   0x08048445 <+40>:    je     0x804844d <foo_ifelse+48>
+ L- z" O( }: r. j8 Q   0x08048447 <+42>:    cmp    BYTE PTR [ebp-0x4],0x62
1 n; g. A' ?% ], @& G0 \+ ^   0x0804844b <+46>:    jne    0x8048459 <foo_ifelse+60>
8 ~0 G: w, k7 V) T$ a( H' C& Q. s. F* x9 Y, W
8                       c += 2;& y$ |$ |4 y& E3 M; z: Y: g9 B
   0x0804844d <+48>:    movzx  eax,BYTE PTR [ebp-0x4]
  [6 x" U6 x4 V! A  i  I7 ]   0x08048451 <+52>:    add    eax,0x23 J8 o4 l4 Z3 n  T# E. f; |
   0x08048454 <+55>:    mov    BYTE PTR [ebp-0x4],al' Y* F9 ~$ P9 P+ \3 q' Y5 A% C& @
   0x08048457 <+58>:    jmp    0x804847b <foo_ifelse+94>
/ T; Z1 u: o/ I& t! A6 J  T
8 F  z4 t4 _# G# p9               } else if (c == 'A' || c == 'B') {- r4 V4 ~0 a4 u: x& y* ~4 |1 T
   0x08048459 <+60>:    cmp    BYTE PTR [ebp-0x4],0x41' y4 v# H2 @# z" F- ~
   0x0804845d <+64>:    je     0x8048465 <foo_ifelse+72>
( C& j( l( o; {9 R# e- g8 ~   0x0804845f <+66>:    cmp    BYTE PTR [ebp-0x4],0x42
0 M) c- B* y% l! m- J8 J1 f+ k   0x08048463 <+70>:    jne    0x8048471 <foo_ifelse+84>/ R  w4 M9 e  T5 ?+ D! L) |8 a
3 _! a/ j; \: d2 l7 T
10                      c += 3;5 M; R6 h" M+ |3 M8 F5 F: l# F1 c
   0x08048465 <+72>:    movzx  eax,BYTE PTR [ebp-0x4]* `3 r& j: B- e6 B# t% r1 R
   0x08048469 <+76>:    add    eax,0x3: ^/ X! u) K' b1 f
   0x0804846c <+79>:    mov    BYTE PTR [ebp-0x4],al; c7 ?5 `/ M4 r
   0x0804846f <+82>:    jmp    0x804847b <foo_ifelse+94>. M1 \5 s5 z- R7 z
+ L7 ?2 ~8 l, K: O9 b
11              } else {# k$ q: o! ]% l# p, A& S# x
12                      c += 4;
( h1 ]' R, z5 j  Z0 k2 g/ Y   0x08048471 <+84>:    movzx  eax,BYTE PTR [ebp-0x4]
) @; \: N1 T! g   0x08048475 <+88>:    add    eax,0x4  d6 d3 i4 s" U, r6 s. V
   0x08048478 <+91>:    mov    BYTE PTR [ebp-0x4],al, w' `9 h3 l, F
( T' S4 U, n) n3 b& X0 M! A+ _
13              }
3 v# M' S$ u0 x  u8 E2 X3 }# N) e14
# v* X0 c+ _" N, P' ?15              return (c);
2 \/ h: {; b5 G; F9 B   0x0804847b <+94>:    movsx  eax,BYTE PTR [ebp-0x4]
* w9 x8 M1 B' e  K" o- e( i- ?9 u1 _% z$ W
16      }
/ C  a/ p7 c* c   0x0804847f <+98>:    leave: ~6 g; ?  {4 S2 a! r
   0x08048480 <+99>:    ret
, }! m1 i: I. O$ g0 z, e( l4 K: s- y9 D1 y
End of assembler dump.- P& |* A7 d! ^+ Q2 a5 ~/ L
(gdb)
9 y! X" C; k' h
o 反汇编foo_switch()
(gdb) set disassembly-flavor intel% J  t; w6 W: u6 W
(gdb) disas /m foo_switch, i3 @3 _" J0 c4 X8 g$ `
Dump of assembler code for function foo_switch:  u. ]2 X& @5 o8 w$ L. b
20      {4 V. ]8 C: M  B: u
   0x08048481 <+0>:     push   ebp+ N6 ~# x2 j9 I1 Q! q0 |
   0x08048482 <+1>:     mov    ebp,esp
  B9 N+ V; x( Z" R   0x08048484 <+3>:     sub    esp,0x4
2 }# e2 V4 f8 k5 I# Z   0x08048487 <+6>:     mov    eax,DWORD PTR [ebp+0x8]
2 G+ O+ O4 E0 |) `" y   0x0804848a <+9>:     mov    BYTE PTR [ebp-0x4],al
( {$ v+ M; ~/ G# @' `: _' m" y9 V$ N# I% s) p
21              switch (c) {3 u( J3 A$ R! H# l
   0x0804848d <+12>:    movsx  eax,BYTE PTR [ebp-0x4]
+ e$ j. |" D; a+ X   0x08048491 <+16>:    sub    eax,0x30
' H/ _; \- A$ q/ ?  [0 f. `( ?   0x08048494 <+19>:    cmp    eax,0x32
8 [( \# ]- o: q4 q4 z9 X3 m% k   0x08048497 <+22>:    ja     0x80484c6 <foo_switch+69>
8 t, m/ S+ w, v1 J6 @) a& v   0x08048499 <+24>:    mov    eax,DWORD PTR [eax*4+0x80485f0]
) j8 \$ x5 K' @3 Q+ \   0x080484a0 <+31>:    jmp    eax
( z$ V0 H% \( G7 r
& s/ y. @# j- n. I$ q22                      case '1':7 q$ [9 O1 x* q: G' ^
23                      case '0': c += 1; break;6 v; `9 |9 |& B, l: ~% L* C" k8 t) `
   0x080484a2 <+33>:    movzx  eax,BYTE PTR [ebp-0x4]
' t9 M( d. k2 k  q. r) H7 G   0x080484a6 <+37>:    add    eax,0x15 V) ]$ Q1 h) n
   0x080484a9 <+40>:    mov    BYTE PTR [ebp-0x4],al5 G: O- s7 Q2 v$ |/ F3 x" S3 k
   0x080484ac <+43>:    jmp    0x80484d1 <foo_switch+80>. E$ D5 u; l$ l

5 P7 D  Y/ E* k4 L24                      case 'b':+ E0 z! H0 V3 A1 N, J7 e) m
25                      case 'a': c += 2; break;# U. r( z" j% w7 \
   0x080484ae <+45>:    movzx  eax,BYTE PTR [ebp-0x4]: t8 K! @2 f: N
   0x080484b2 <+49>:    add    eax,0x2& S# t6 B( h% X! _1 J- \
   0x080484b5 <+52>:    mov    BYTE PTR [ebp-0x4],al$ u; p6 \6 K# i& M4 s. W% h: E/ R
   0x080484b8 <+55>:    jmp    0x80484d1 <foo_switch+80>
$ ?8 c4 m4 d8 ~; s' k6 N6 m% M& @. M5 b7 s' `7 f
26                      case 'B':9 g: Y+ f: t. {2 K$ U, ^# ?
27                      case 'A': c += 3; break;8 k+ s. W! J* o) W  [) S  c! P
   0x080484ba <+57>:    movzx  eax,BYTE PTR [ebp-0x4]# D1 K2 P3 s; y) O
   0x080484be <+61>:    add    eax,0x3' _+ z: f1 o- x
   0x080484c1 <+64>:    mov    BYTE PTR [ebp-0x4],al" R- L% N" T: I" J
   0x080484c4 <+67>:    jmp    0x80484d1 <foo_switch+80>2 G' }+ d( y! O

2 b  S) o  x" P/ c( N+ B28                      default:  c += 4; break;
5 W8 C/ m+ {3 H$ `- }   0x080484c6 <+69>:    movzx  eax,BYTE PTR [ebp-0x4]
. a* [/ V; L" ^5 e" k   0x080484ca <+73>:    add    eax,0x4; X: W" a2 D2 D5 k
   0x080484cd <+76>:    mov    BYTE PTR [ebp-0x4],al/ R& v8 m" x* N7 C
   0x080484d0 <+79>:    nop9 \0 `" U2 c' O! Z* j1 y
& V/ s! D0 M# E& A# F
29              }
. b) u7 p0 x$ o3 g% `30
$ }2 [: B$ ^* W  Y, z31              return (c);
+ ^6 b+ b0 S# X   0x080484d1 <+80>:    movsx  eax,BYTE PTR [ebp-0x4]
& w9 q; B% ?1 \! w" G& V! v5 V0 {
' u' _# L7 k1 l' {32      }# Y) M0 ~( E9 H5 w8 u
   0x080484d5 <+84>:    leave9 s3 s& ~' F8 a) r
   0x080484d6 <+85>:    ret
' o3 F  o- U6 Q
; z9 N3 Y4 h3 G. JEnd of assembler dump.6 ~4 c. O7 B5 X( r
(gdb)
2 [/ S8 a( I/ q9 U' U
分析:
  • 在foo_ifelse()中,采用的方法是按顺序比较,如满足条件,则执行对应的代码,否则跳转到下一个分支再进行比较;
  • 在foo_switch()中,下面的这段汇编代码比较有意思,

    % G! L, h: l. p' ?2 A
..
7 z, A4 p, N# s: ?- ^" F21 switch (c) {
' \  u! r. J( R% C$ u0 y* J: k   0x0804848d <+12>:    movsx  eax,BYTE PTR [ebp-0x4]
/ F3 k7 c' [. r: q% |8 q: {+ S   0x08048491 <+16>:    sub    eax,0x30
+ S4 d6 f: ]) Q& \6 n0 y0 n   0x08048494 <+19>:    cmp    eax,0x32; i- e  N7 p. ?/ S
   0x08048497 <+22>:    ja     0x80484c6 <foo_switch+69>+ {8 q+ z0 y, h" B
   0x08048499 <+24>:    mov    eax,DWORD PTR [eax*4+0x80485f0]
) D( Y9 E6 Q9 p9 g   0x080484a0 <+31>:    jmp    eax
% m8 \+ |0 K1 t0 m0 S: w0 t3 Z..
/ G% q7 R' [7 h& K  a+ e6 d
注意:
第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;
    ; A4 F6 C- `7 F& x: I: b
通过gdb查看对应的内存,确实如此!
>>> ord('1') - 0x30
; [, Y: ]# d% Y3 Q8 D; G; M>>> ord('0') - 0x309 d7 E6 e  B- b. d) N# S- n
(gdb) x /2wx  0*4+0x80485f0( r/ n4 ?- L2 j& e) y: u3 b/ B
0x80485f0:    0x080484a2    0x080484a2
+ F  c. v3 h' n+ L# {9 o3 @8 y9 Q, M, c
>>> ord('b') - 0x30
8 H8 p. k; g% q1 M, k, k0 [>>> ord('a') - 0x305 s7 R: c) A* g) W: O# c: I
(gdb) x /2wx 49*4+0x80485f08 y# }' o. W* S9 O+ N) W& [7 O- J! h
0x80486b4:    0x080484ae    0x080484ae
. z+ w% d  O2 l2 b& {                & X$ m5 ]0 \% C& h$ Y: ]7 e& U5 z' y
>>> ord('B') - 0x30
# e5 \+ b# d1 u- U+ `>>> ord('A') - 0x30# d3 x: {( L, C8 r& `+ i/ ]
(gdb) x /2wx 17*4+0x80485f0
7 q4 N8 H0 Q; `- N# N+ P4 G6 x8 R  V0x8048634:    0x080484ba    0x080484ba

4 ]( ]; x/ V5 J' M+ C* i
那么,我们可以大胆的猜测,虽然c的取值不同但是跳转的IP确实是精准无误的,一定是编译阶段就被设定好了,果真如此吗?接下来分析一下对应的二进制文件foo,
第四步,使用objdump查看foo,$ objdump -D foo > /tmp/x! J, r( K  G4 a" m* H& S

# L% v+ u7 _! \; `; n- m3 {$ vim /tmp/x
2 S  [6 m) T- l" G/ M$ c 509 Disassembly of section .rodata:/ Y" C0 F) z; G# k
...
  t9 n* g4 A- i 518  80485f0:       a2 84 04 08 a2          mov    %al,0xa2080484
2 {& N/ e' |, {" V/ E 519  80485f5:       84 04 08                test   %al,(%eax,%ecx,1)
+ g. L, m8 |$ R* U1 ` ...6 z* i2 G6 Q7 F4 f! A
534  8048630:       c6 84 04 08 ba 84 04    movb   $0x8,0x484ba08(%esp,%eax,1)" H7 V1 @) c: Q% [% [8 r/ y# q0 w
535  8048637:       081 }, `% Z4 M& g8 J
536  8048638:       ba 84 04 08 c6          mov    $0xc6080484,%edx* h: B& a. m( g4 }
...+ }8 g2 p1 r% k( h
566  80486b0:       c6 84 04 08 ae 84 04    movb   $0x8,0x484ae08(%esp,%eax,1)
: {* |8 N  d* l9 u2 k0 ~% M3 h 567  80486b7:       08! k  C" V6 {& T: {( U
568  80486b8:       ae                      scas   %es%edi),%al6 g; ^7 \9 m! F" [- |2 @. h/ j
569  80486b9:       84 04 08                test   %al,(%eax,%ecx,1)2 }; g% W6 T' ]! h9 [" A
...

: E! ]6 e& }1 f  \0 n, ?$ A8 ?5 T+ Q
在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]+ M  h8 j* K# K. I( E: f- \
17    0x080484a0 <+31>:    jmp    eax
+ z$ \8 W( ^( }7 W
到此为止,我们已经搞清楚了为什么switch...case...语句相对于if...else if...else...来说执行效率要高的根本原因。简言之,编译的时候创建了一个map存于.rodata区中,运行的时候直接根据输入(c的值)查表,找到对应的IP后直接跳转。(省去了cmp, jmp -> cmp, jmp -> cmp, jmp...这一冗长的计算过程。)
总结:
switch...case...执行效率高,属于典型的以空间换时间。也就是说,(套用算法的行话)以提高空间复杂度为代价降低了时间复杂度。
! u+ [% R" Q/ V
赞 收藏 评论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 手机版