在我们平常的编程当中,用于判断的地方很多,但主要有下面三种方式:if-else;switch;?:。其中最后一种方式在本质上和if-else是一样的。switch和if-else其实也一样,如果我们把switch改成if(...) {} else if(...) {} else {},那么你实现的效果和switch实际差不多,熟悉的朋友都会有这样的体验。或许有的朋友还是不太相信,大家可以自己用写实例比较看一下。

  21: int m=10;

  004017A8 mov dword ptr [ebp-4],0Ah

  22: switch(m)

  23: {

  004017AF mov eax,dword ptr [ebp-4]

  004017B2 mov dword ptr [ebp-8],eax

  004017B5 cmp dword ptr [ebp-8],0Ah

  004017B9 je process+33h (004017c3)

  004017BB cmp dword ptr [ebp-8],0Bh

  004017BF je process+42h (004017d2)

  004017C1 jmp process+4Fh (004017df)

  24: case 10:

  25: printf("ten!

  ");

  004017C3 push offset string "ten!

  " (0046f028)

  004017C8 call printf (004214d0)

  004017CD add esp,4

  26: break;

  004017D0 jmp process+4Fh (004017df)

  27:

  28: case 11:

  29: printf("eleven!

  ");

  004017D2 push offset string "eleven!

  " (0046f01c)

  004017D7 call printf (004214d0)

  004017DC add esp,4

  30: break;

  31:

  32: default:

  33: break;

  34: }

  35: return;

  36: }

  上面的汇编代码说明了有break的时候,函数是怎么编译的。我们看到从地址0x4017AF处, CPU开始集中对m进行判断。首先,m的数据被赋值到eax,然后eax拷贝到堆栈【ebp-8】的内存当中。接着开始比较数据10,即16进制0A,如果两者相等,代码跳转到0x4017C3处理;但是如果不相等呢,那么指令会按照原来的排列顺序继续向下走,和11继续比较,如果比较成功,那么就会到地址0x4017d2处执行,如果都不相等,那么只好跳出switch减肥模块,到地址0x4017df处执行了。前面我们说到,如果数据和10或者11比较成功的话,那么就会跳转到相应的case语句处继续执行,可是比较结束后,还会跳转到原来的位置吗?汇编代码告诉我们,他们不会。因为case比较结束后,也要到地址ox4017df处报到。 如果这里case10后面没有break呢?情况会不会不一样呢?

  004017A8 mov dword ptr [ebp-4],0Ah

  22: switch(m)

  23: {

  004017AF mov eax,dword ptr [ebp-4]

  004017B2 mov dword ptr [ebp-8],eax

  004017B5 cmp dword ptr [ebp-8],0Ah

  004017B9 je process+33h (004017c3)

  004017BB cmp dword ptr [ebp-8],0Bh

  004017BF je process+40h (004017d0)

  004017C1 jmp process+4Dh (004017dd)

  24: case 10:

  25: printf("ten!

  ");

  004017C3 push offset string "ten!

  " (0046f028)

  004017C8 call printf (004214d0)

  004017CD add esp,4

  26:

  27: case 11:

  28: printf("eleven!

  ");

  004017D0 push offset string "eleven!

  " (0046f01c)

  004017D5 call printf (004214d0)

  004017DA add esp,4

  29: break;

  30:

  31: default:

  32: break;

  33: }

  34: return;

  35: }

  我们在case10后面取消了break语句。和原来的情形稍有不同,我们发现case10在执行了printf函数之后,并没有跳出当前的switch模块,而是继续执行case11的语句。所以在输出结果的时候,你会发现ten和eleven都有打印。看了这样清晰的流程之后,相信你对break的重要性又有了新的认识。在汇编面前,一切都一目了然。

  同样,我们可以看下面一段示例。

  21: int m=10;

  004017A8 mov dword ptr [ebp-4],0Ah

  22: int n=0;

  004017AF mov dword ptr [ebp-8],0

  23:

  24: if(m==10 && n==0)

  004017B6 cmp dword ptr [ebp-4],0Ah

  004017BA jne process+3Fh (004017cf)

  004017BC cmp dword ptr [ebp-8],0

  004017C0 jne process+3Fh (004017cf)

  25: {

  26: printf("&&!

  ");

  004017C2 push offset string "&&!

  " (0046f020)

  004017C7 call printf (004214e0)

  004017CC add esp,4

  27: }

  28:

  29: if(m==10 || n==0)

  004017CF cmp dword ptr [ebp-4],0Ah

  004017D3 je process+4Bh (004017db)

  004017D5 cmp dword ptr [ebp-8],0

  004017D9 jne process+58h (004017e8)

  30: {

  31: printf("||");

  004017DB push offset string "||" (0046f01c)

  004017E0 call printf (004214e0)

  004017E5 add esp,4

  32: }

  &&在C语言中是与的意思,而||是或的意思。与就是说,两者均为真;而或的意思是两方中一方为真即可。这在对应的汇编的语句上面也体现得淋漓尽致。首先我们看与的情形。从地址0x4017B6处地指令,我们发现首先比较的是m数据,然后比较的是n数据,这可以从他们在堆栈中的偏移值可以看出来。如果m和10比较,那么下面才有n和0比较的机会,一旦比较失败,就会跳转到地址0x4017cf处执行,跳出当前的判断模块。n和0比较也一样,只有两者都比较成功,才有机会进入地址0x4017C2处执行,打印&&。和与对应的是或,我们发现地址0x4017cf处开始比较的也是数据m,其次才是数据n。和与不同,m数据和n数据只要由一方成功,就会跳转到地址0x4017db处执行。只有两者都为假,才会跳出当前的模块。所以说,两个jne构成了与的基础,一个je和一个jne构成了或的基石。大家可以自己试试看如果&&的选项和||的选项不断进行叠加的时候会出现怎样的情形?试试看。

  if-else这种二分判断结构其实在现在的编程中特别重要,也特别基础。有一个关于二分法最显著的代码就是在顺序数据中进行二分查找。如果有兴趣的话,自己可以动笔试试?在这里,我有几个建议:

  (1)确保函数输入的索引范围有序 (start < end)

  (2)在计算中间点的时候注意不要范围溢出 (middle=start + (end - start) >> 1)

  (3)考虑把你的函数修改成通用的二分法查找函数(可以考虑用const void*和函数指针)


更多相关文章

  1. 用PDO扩展连接mysql数据库 实现用户登录时防sql注入的处理
  2. 【北亚数据恢复】输入错误命令导致MySQL数据库全表数据丢失的数
  3. mysql使用PDO预处理访问数据库防sql语句注入
  4. android sqlite 一次创建多个表
  5. android 操作sqllite 数据库
  6. Android(安卓)sqlite 数据库在java代码中的增删改查
  7. android访问mysql增删改查_Android(安卓)Studio连接数据库实现增
  8. Android(安卓)Intents and Intent Filters
  9. Android腾讯微薄客户端开发十三:提及篇(与我有关的微博)

随机推荐

  1. 找到多维Python数组的维度[重复]
  2. 怎么统计android编译时候的warning警告的
  3. Python十题(第2课)
  4. 流畅python学习笔记:第十二章:子类化内置类
  5. Pandas concat:ValueError:传递值的形状为b
  6. Python中类的使用(5私有属性)
  7. 求助:Python是否可以用一行代码来同时给变
  8. 【Python】torrentParser1.04 增加获得磁
  9. Python常用模块-摘要算法(hashlib)
  10. python魔法方法、构造函数、序列与映射、