java switch case 跳转_switch case的跳转原理?

我们可以直接通过汇编代码来看:

void bob()

{

int a = 1;

switch(a)

{

case 1:

std::cout<

case 2:

std::cout<

case 3:

std::cout<

default:

std::cout<

}

}

汇编其中关键代码如下:可以看到case实际的比较顺序是2->3->1, 并不是代码的编写顺序:

0000000000400846 <_z3bobv>:

400846:>..55 >..push %rbp

400847:>..48 89 e5 >..mov %rsp,%rbp

40084a:>..48 83 ec 10 >..sub $0x10,%rsp

40084e:>..c7 45 fc 01 00 00 00 >..movl $0x1,-0x4(%rbp)

400855:>..8b 45 fc >..mov -0x4(%rbp),%eax

//先比较是否等于2, 如果是,则跳转到0x400885指令地址, 即case的输出语句;

400858:>..83 f8 02 >..cmp $0x2,%eax

40085b:>..74 28 >..je 400885 <_z3bobv>

//否则,继续比较是否等于3,

40085d:>..83 f8 03 >..cmp $0x3,%eax

400860:>..74 41 >..je 4008a3 <_z3bobv>

//最后比较是否等于1

400862:>..83 f8 01 >..cmp $0x1,%eax

400865:>..75 5a >..jne 4008c1 <_z3bobv>

400867:>..be d4 09 40 00 >..mov $0x4009d4,%esi

40086c:>..bf 60 10 60 00 >..mov $0x601060,%edi

400871:>..e8 9a fe ff ff >..callq 400710 <_zstlsist11char_traitsiceerst13basic_ostreamict_>

400876:>..be 30 07 40 00 >..mov $0x400730,%esi

40087b:>..48 89 c7 >..mov %rax,%rdi

40087e:>..e8 9d fe ff ff >..callq 400720 <_znsolsepfrsos_e>

400883:>..eb 58 >..jmp 4008dd <_z3bobv>

400885:>..be db 09 40 00 >..mov $0x4009db,%esi

40088a:>..bf 60 10 60 00 >..mov $0x601060,%edi

40088f:>..e8 7c fe ff ff >..callq 400710 <_zstlsist11char_traitsiceerst13basic_ostreamict_>

400894:>..be 30 07 40 00 >..mov $0x400730,%esi

400899:>..48 89 c7 >..mov %rax,%rdi

40089c:>..e8 7f fe ff ff >..callq 400720 <_znsolsepfrsos_e>

4008a1:>..eb 3a >..jmp 4008dd <_z3bobv>

4008a3:>..be db 09 40 00 >..mov $0x4009db,%esi

4008a8:>..bf 60 10 60 00 >..mov $0x601060,%edi

4008ad:>..e8 5e fe ff ff >..callq 400710 <_zstlsist11char_traitsiceerst13basic_ostreamict_>

4008b2:>..be 30 07 40 00 >..mov $0x400730,%esi

4008b7:>..48 89 c7 >..mov %rax,%rdi

4008ba:>..e8 61 fe ff ff >..callq 400720 <_znsolsepfrsos_e>

4008bf:>..eb 1c >..jmp 4008dd <_z3bobv>

4008c1:>..be e2 09 40 00 >..mov $0x4009e2,%esi

40092c: c9 leaveq

40092d: c3 retq

至于为什么是这个顺序,没有研究过,这是编译器层面的处理了,可以一起学习下,

第二次修改

@felix ,这里上面特意没有开启优化,因为是要看原理,所以优化就没有什么意思,下面是开始O2选项的结果:

00000000004009a0 <_z3bobv>:

4009a0: 53 push %rbx

4009a1: ba 06 00 00 00 mov $0x6,%edx

// switch case 直接被优化成了下面三句, esi和edi是ostream的两个参数,

4009a6: be b4 0a 40 00 mov $0x400ab4,%esi //这个是rodata段的"case 1"字符串

4009ab: bf 80 10 60 00 mov $0x601080,%edi //std::cout对象

//直接调用输出语句

4009b0: e8 6b fe ff ff callq 400820 <_zst16__ostream_inserticst11char_traitsiceerst13basic_ostreamit_t0_es6_pks3_l>

4009b5: 48 8b 05 c4 06 20 00 mov 0x2006c4(%rip),%rax # 601080 <_zst4cout>

4009bc: 48 8b 40 e8 mov -0x18(%rax),%rax

4009c0: 48 8b 98 70 11 60 00 mov 0x601170(%rax),%rbx

4009c7: 48 85 db test %rbx,%rbx

4009ca: 74 4a je 400a16 <_z3bobv>

4009cc: 80 7b 38 00 cmpb $0x0,0x38(%rbx)

4009d0: 74 1e je 4009f0 <_z3bobv>

4009d2: 0f be 73 43 movsbl 0x43(%rbx),%esi

4009d6: bf 80 10 60 00 mov $0x601080,%edi

4009db: e8 60 fe ff ff callq 400840 <_znso3putec>

4009e0: 5b pop %rbx

4009e1: 48 89 c7 mov %rax,%rdi

4009e4: e9 47 fe ff ff jmpq 400830 <_znso5flushev>

4009e9: 0f 1f 80 00 00 00 00 nopl 0x0(%rax)

4009f0: 48 89 df mov %rbx,%rdi

4009f3: e8 d8 fd ff ff callq 4007d0 <_znkst5ctypeice13_m_widen_initev>

4009f8: 48 8b 03 mov (%rbx),%rax

4009fb: be 0a 00 00 00 mov $0xa,%esi

400a00: 48 8b 40 30 mov 0x30(%rax),%rax

400a04: 48 3d 20 0a 40 00 cmp $0x400a20,%rax

400a0a: 74 ca je 4009d6 <_z3bobv>

400a0c: 48 89 df mov %rbx,%rdi

400a0f: ff d0 callq *%rax

400a11: 0f be f0 movsbl %al,%esi

400a14: eb c0 jmp 4009d6 <_z3bobv>

400a16: e8 a5 fd ff ff callq 4007c0 <_zst16__throw_bad_castv>

400a1b: 0f 1f 44 00 00 nopl 0x0(%rax,%rax,1)

下面是rodata中保存的要输出的“case 1”字符串, 在不优化的情况下,rodata中是会有"case 2" , "case 3", "default case"的。

Contents of section .rodata:

400ab0 01000200 63617365 203100 ....case 1.

第三次修改

@felix ,谢谢, 当case语句增加到10条(具体多少开启,可以测一下)时,编译器(debug和O2)开启了case 的匹配优化,也就是大家所谓的jump table:

.LC0:

6 >....string>"case 1"

7 .LC1:

8 >....string>"case 2"

9 .LC2:

10 >....string>"case 3"

11 .LC3:

12 >....string>"case 4"

13 .LC4:

14 >....string>"case 5"

15 .LC5:

16 >....string>"case 6"

17 .LC6:

18 >....string>"case 7"

19 .LC7:

20 >....string>"case 8"

21 .LC8:

22 >....string>"case 9"

23 .LC9:

24 >....string>"case 10"

25 .LC10:

26 >....string>"default case"

27 >....text

28 >....globl>._Z3bobv

29 >....type>.._Z3bobv, @function

30 _Z3bobv:

31 .LFB1021:

32 >....cfi_startproc

33 >...pushq>..%rbp

34 >....cfi_def_cfa_offset 16

35 >....cfi_offset 6, -16

36 >...movq>...%rsp, %rbp

37 >....cfi_def_cfa_register 6

38 >...subq>...$16, %rsp

39 >...movl>...$1, -4(%rbp)

// 如果输入的参数 大于10, 直接进入default的处理

40 >...cmpl>...$10, -4(%rbp)

41 >...ja>..L2

// 将输入参数存入eax寄存器中, 然后通过L4段,计算出匹配case的地址,进行跳转

42 >...movl>...-4(%rbp), %eax

43 >...movq>....L4(,%rax,8), %rax

44 >...jmp>*%rax

45 >....section>....rodata

46 >....align 8

47 >....align 4

//这部分就是jump table, 根据case的参数进行偏移

48 .L4:

49 >....quad>...L2 //default

50 >....quad>...L3 //case 1

51 >....quad>...L5

52 >....quad>...L6

53 >....quad>...L7

54 >....quad>...L8

55 >....quad>...L9

56 >....quad>...L10

57 >....quad>...L11

58 >....quad>...L12

59 >....quad>...L13

60 >....text

61 .L3:

62 >...movl>...$.LC0, %esi

63 >...movl>...$_ZSt4cout, %edi

64 >...call>..._ZStlsISt11char_traitsIcEERSt13basic_ostreamIcT_ES5_PKc

65 >...movl>...$_ZSt4endlIcSt11char_traitsIcEERSt13basic_ostreamIT_T0_ES6_, %esi

66 >...movq>...%rax, %rdi

67 >...call>..._ZNSolsEPFRSoS_E

68 >...jmp>.L14

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值