研究研究看似简单的switch问题

43 篇文章 0 订阅
8 篇文章 0 订阅

这个问题,之前一直没有去思考,在c和指针这本书中,在谈到switch是给出了这样一段话:
这里写图片描述
也就是说switch的条件要是整型值?好像一直这么用,却没有研究过为什么。惭愧。。。
研究c语言最好就是看其汇编实现,下面在linux 64位上实验。
这里写图片描述
编译运行,输出10;
然后将目标文件dump出来

int main()
{
  40052d:   55                      push   %rbp
  40052e:   48 89 e5                mov    %rsp,%rbp
  400531:   48 83 ec 10             sub    $0x10,%rsp
int a=10;
  400535:   c7 45 fc 0a 00 00 00    movl   $0xa,-0x4(%rbp)
switch(a)
  40053c:   8b 45 fc                mov    -0x4(%rbp),%eax
  40053f:   83 f8 01                cmp    $0x1,%eax
  400542:   74 07                   je     40054b <main+0x1e>
  400544:   83 f8 0a                cmp    $0xa,%eax
  400547:   74 0e                   je     400557 <main+0x2a>
  400549:   eb 18                   jmp    400563 <main+0x36>
{
case 1: 
printf("1\n");
  40054b:   bf 04 06 40 00          mov    $0x400604,%edi
  400550:   e8 bb fe ff ff          callq  400410 <puts@plt>
break;
  400555:   eb 17                   jmp    40056e <main+0x41>
case 10:
printf("10\n");
  400557:   bf 06 06 40 00          mov    $0x400606,%edi
  40055c:   e8 af fe ff ff          callq  400410 <puts@plt>
break;
  400561:   eb 0b                   jmp    40056e <main+0x41>
default:
printf("default\n");
  400563:   bf 09 06 40 00          mov    $0x400609,%edi
  400568:   e8 a3 fe ff ff          callq  400410 <puts@plt>
break;
  40056d:   90                      nop
}
return 0;
  40056e:   b8 00 00 00 00          mov    $0x0,%eax
}

主要看看这段代码,就是switch一句代码展开好多汇编代码

switch(a)
  40053c:   8b 45 fc                mov    -0x4(%rbp),%eax
  40053f:   83 f8 01                cmp    $0x1,%eax
  400542:   74 07                   je     40054b <main+0x1e>
  400544:   83 f8 0a                cmp    $0xa,%eax
  400547:   74 0e                   je     400557 <main+0x2a>
  400549:   eb 18                   jmp    400563 <main+0x36>

将a的值存放到寄存器eax中,然后出现两个比较三个挑战,就是下面的两个case 条件,
是否可以就此下结论,switch的实现如上所述,翻开深入理解计算机系统,有一节讲的switch,而上面说的是switch的实现是利用跳转表,而我的测试代码却没有。
抛开这个问题不谈,使用比较跳转这样的实现方式是不是有点效率偏低。如果有n多个case比较,那不是死定了。。。而深入计算机书中讲的的是一种查表法(好吧,这让我想起了FPGA芯片的实现方式)。
是不是gcc会根据case多少自己优化?
接下来将case加多,编写实验,将打印语句去掉。
这里写图片描述
然后dump出目标文件,如下

int main()
{
  4004ed:   55                   push   %rbp
  4004ee:   48 89 e5             mov    %rsp,%rbp
 int a=10;
  4004f1:   c7 45 fc 0a 00 00 00 movl   $0xa,-0x4(%rbp)
 switch(a)
  4004f8:   8b 45 fc             mov    -0x4(%rbp),%eax
  4004fb:   83 e8 0a             sub    $0xa,%eax
  4004fe:   83 f8 07             cmp    $0x7,%eax
  400501:   77 0c                   ja     40050f <main+0x22>
  400503:   89 c0                   mov    %eax,%eax
  400505:   48 8b 04 c5 a8 05 40 mov    0x4005a8(,%rax,8),%rax
  40050c:   00 
  40050d:   ff e0                   jmpq   *%rax
  case 16:
   break;
  case 17:
   break;
  default:
   break;
  40050f:   90                   nop
  400510:   eb 01                   jmp    400513 <main+0x26>
  case 15:
   break;
  case 16:
   break;
  case 17:
   break;
  400512:   90                   nop
  default:
   break;
 }
 return 0;
  400513:   b8 00 00 00 00       mov    $0x0,%eax
}

令人发指的事情出现了。。看看这段代码

switch(a)
  4004f8:   8b 45 fc             mov    -0x4(%rbp),%eax
  4004fb:   83 e8 0a             sub    $0xa,%eax
  4004fe:   83 f8 07             cmp    $0x7,%eax
  400501:   77 0c                   ja     40050f <main+0x22>
  400503:   89 c0                   mov    %eax,%eax
  400505:   48 8b 04 c5 a8 05 40 mov    0x4005a8(,%rax,8),%rax
  40050c:   00
  40050d:   ff e0                   jmpq   *%rax

并没有出现依次比较跳转的实现方式。
f8处代码,将a的值存放到寄存器eax中
fb代码,将eax的值减去10;实际这是一种映射思想,
下面一个比较和跳转是判断eax的值超过7的,对应default的情况。
再下面的mov和jmpq就是重要的问题了。
看看这个汇编代码
mov 0x4005a8(,%rax,8),%rax
看看这句代码有一种似曾相识的感觉呀。

脑洞大开中。。。。。
bingo
在linux v0.12 源码中看到过这个语法,在研究系统函数时看到的。
那篇博客链接为http://blog.csdn.net/u010442328/article/details/46812049
其地址计算为0x4005a8+rax×8,前面面是数组的基地址,rax为数组的下标,8是每个数组元素占的字节数。

原来switch是这样实现的。
那么问题来了,switch条件为什么只能是整型值?
什么是整型值?

从switch实现的机制上来说,判断条件是作为数组的下标的,用于查表的,这一点上是满足判断条件必须为整数的。是不是还有什么深层次的原因呢?

看看 gcc编译器会有什么答案,看一段代码

int main()
{
 switch("abc")
 {
  case "abc":
   break;
  default:
   break;
 }

 return 0;
}

gcc编译为
这里写图片描述
从编译结果看并没有什么更深入的线索,
根据gcc给出的error,谷歌吧
在stackoverflow中也有人研究这个问题。
链接http://stackoverflow.com/questions/650162/why-switch-statement-cannot-be-applied-on-strings
其中一个解释为
The reason why has to do with the type system. C/C++ doesn’t really support strings as a type. It does support the idea of a constant char array but it doesn’t really fully understand the notion of a string.
In order to generate the code for a switch statement the compiler must understand what it means for two values to be equal. For items like ints and enums, this is a trivial bit comparison. But how should the compiler compare 2 string values? Case sensitive, insensitive, culture aware, etc … Without a full awareness of a string this cannot be accurately answered.
Additionally, C/C++ switch statements are typically generated as branch tables. It’s not nearly as easy to generate a branch table for a string style switch.
在其他语言中,是有支持的非整型值的switch,但是在c中并没有支持。这个问题保留。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值