switch没有default_通过调试观察 if 和 switch

        利用 VC++ 6.0 调试

        编写程序,定义 n = 123,打印 Hello world! 

#include int main(){  int n = 123;  printf("Hello world!\r\n");  return 0;}

        按 F7 进行编译,F9 下断点(运行到短点停下来),F5 运行, shift + F5 终止调试。

        按 F10 进行单步跳过调试。进入调式模式。黄色箭头的指向,表示程序流程的位置,指的是即将执行但尚未执行的代码行。这时左花括号还未执行 { 。

ee5da2665a48ced3413f66957284d2db.png

        此时在观察框,输入 n 和 &n,提示:CXX0069:Error:variable needs stack Frame(错误:变量需要堆框架)。

f10548e63473bd090525b2de6b794724.png

        再按 F10 ,程序执行左花括号 {  完毕 ,&n 是 0x19ff2c ,n 的值还没有初始化

bc940d4bae52d39b1bfaab5f339ecc50.png

        再按 F10 ,int n = 123 执行完毕,此时 n 的值是 0x0000007b,也就是 10 进制的 123

ae82531203f4106a207a433ebe6cf0bb.png

         此时,黄色箭头指向了 printf() 函数

a6d099e3018be0c692b0d3e021c4b435.png

        这时有两种选择,一种是 F10 单步跳过调试,直接到 return 0,另一种是 F11 单步进入调试,进入到 printf 函数的内部。这里按 F11 进入 printf 函数的内部 。进入 printf 函数的内部后,继续 F11 进入了 _ftbuf 函数的内部,继续 F11 进入了 _flush 函数的内部,继续 F11 进入了 _write 函数内部。如果不记得来时的路,打开栈窗口,可以看到此时在 _write 函数,是从 _flush 函数进来的,依次类推。

371206ad5b3f138c934c2928f5481ae4.png

         跳回上一个函数,按 shift + F11 

d03c2c35fa32d61b6e7c6983a339d368.png

        跳回多层函数。通过下断点运行的方式,跳回 main 函数的 printf 函数位置。双击 main 函数,在 printf 函数的位置按 F9 下断点

89d4f6257645c7468aa504d5662f36c5.png

         按 F5 执行,就会跳到 printf 函数的位置

dfcc0b27502c0d3fa7db616b29974bb3.png

        分支语句 if

        编写一个多分支程序,判断 123 除以 5 的余数是多少

#include int main(){  int n = 123;  if(n % 5 == 0)  {    printf("n % 5 == 0");  }  else if(n % 5 == 1)  {    printf("n % 5 == 1");  }  else if(n % 5 == 2)  {    printf("n % 5 == 2");  }  else if(n % 5 == 3)  {    printf("n % 5 == 3");  }  else  {    printf("n % 5 == 4");  }  return 0;}

        按 F10 进入单步调试模式

261afabc78a10a8c1bf45bc40d2f359c.png

        此时在观察框输入 n 和 &n 以及 n % 5 ,提示:CXX0069:Error:variable needs stack Frame(错误:变量需要堆框架)。

6bb9047d3cdc57d107ad07dd65359342.png

        再按 F10 执行完左花括号 {

aeb6f9457db730b3d0f2121a53efb359.png

        此时 n 的值是 0xcccccccc,&n 的值是 0x0019ff2c,n % 5 的值是 0x00000000

8986d5ad68ce14fc9ca4d5eccc007076.png

        再按 F10 执行完 int n = 123

c1a274b582825bda2bc8afb9978e3915.png

        此时 n 的值是 0x0000007b ,&n 的值是 0x0019ff2c ,n % 3 的值是 0x00000003

e74405d36cff9cf4a34b554583dec297.png

        再按 F10 ,因为 if(n % 5 == 0) 不成立,直接跳到 else if (n % 5 == 1)

0cbcb4c665168476d0e2006896116ab6.png

        再按 F10 ,因为 else if(n % 5 == 1) 不成立,直接跳到 else if (n % 5 == 2)

fddf9a61ec5ca9956bcff3223a93bc3b.png

        再按 F10 ,因为 else if(n % 5 == 2) 不成立,直接跳到 else if (n % 5 == 3)

1f0c719974e36d8d9e2c205dad5890bd.png

        再按 F10 ,因为 else if(n % 5 == 3) 成立,进入到分支条件内部 printf("n % 5 == 3")

042295f5f40da2f35814855cea2b0f97.png

        再按 F10,跳出 else if(n % 5 == 3) 的分支,进入到 else 

3cc723d4c9318130ad0e83be5d9800d6.png

        再按 F10 ,因为 else 不成立,进入到 reutrn 0

e41aa116cef1cf6c83a06f9671e08670.png

        通过上面的调试过程,可以看出。虽然很早程序就知道了 123 % 5 == 3,但是还是按顺序执行。从时间成本和系统执行次数来看,放在前面的 if 所需的代价是很小的,但是越往后面的 else if ,else 所需代价就越来越大。 

        分支语句 switch

        调试 switch 

        编写一个 switch 多分支程序,判断 123 除以 5 的余数是多少,其中 case 2,case 3 不加 break

#include int main(){  int n = 123;  switch(n % 5)  {  case 0:    printf("case 0\r\n");    break;  default:    printf("default\r\n");  case 1:    printf("case 1\r\n");    break;  case 2:    printf("case 2\r\n");  case 3:    printf("case 3\r\n");  case 4:    printf("case 4\r\n");    break;  }   return 0;}

        按 F10 进入单步调试

f3d833777cc49eab018089676cb6c2ca.png

        此时在观察框输入 n 和 &n 以及 n % 5 ,提示:CXX0069:Error:variable needs stack Frame(错误:变量需要堆框架)。

74ddaca6ab1976cc0cde9ffec1401f9c.png

        再按 F10 ,左花括号执行完毕

0f411f8ffeea8d8ff3c0bc450db03c40.png

        此时 n 的值是 0xcccccccc,&n 的值是 0x0019ff2c,n % 5 的值是 0x00000000

cc0582a6608e65dfb70bebaf9dbff6a8.png

        再按 F10,int n = 123 执行完毕

7592997cb06f609f7c099028093aa60b.png

        此时 n 的值是 0x0000007b ,&n 的值是 0x0019ff2c ,n%5 的值是 0x00000003

7c6731fb6b8b783214c73787004b5716.png

        再按 F10 ,直接跳入 case 3中,即将执行 printf("case 3\r\n")

89376097d1466ad3de41c00e1e26aa75.png

        因为 case3 没有 break ,再按 F10 ,直接跳入 case 4中,即将执行 printf("case 4\r\n")

96bd7724b866ccd56280d4822c6f35ab.png

        switch 机制

        通过上面的调试过程可以看到,当算出 n % 5 == 3 时,并没有按顺序执行 case 0 ,default,case 1,case 2,而是直接跳到了 case 3 处,然后执行了 case 4。所有的 case 代价都是均等的,没有在前面就优先的机制。

        接下来说一下,switch 较 case 做了哪些优化。这里只说优化的其中一点。每个 case 都有自己的地址,不一定是有序的,因为 case 的顺序是由程序员来决定的。如果 case 的数据顺序连续,switch 的机制中会做个表,这个表就是数组,下标按 0 1 2 3 顺序排 。数组 0 记录了 case 0 的地址,数组 1 记录了 case 1 的地址,数组 2 记录了 case 2 的地址,以此类推。如果 case 的数据不连续,断的地方用 default 代替。当 switch 算出值之后,求出值是什么,就在数组中取这个值的地址,然后流程转到这个地址去。

800d96720f46662c4387f5124794f637.png

        明白了 switch 机制后,按 F10 调式,在 switch 左括号 { 处按 alt + 8 进入反汇编模式。找到最后一串代码,最后一串数字,是 40D792h

c7f7afd8ffeda515f806f7bce96080c8.png

        在地址窗口输入 40D792h,里面的值是 00 40 D7 2D ,00 40 D7 49 ,00 40 D7 58 ,00 40 D7 65 ,00 40 D7 72 。一共 5 个,后面就没有了。

64b7b812ddd938a70fab27b6a0ae4227.png

        case 0 的地址是 00 40 D7 2D

e848ff091bb376d7a2b5d90ccea51818.png

        case 1 的地址是 00 40 D7 49 , case 2 的地址是 00 40 D7 58 ,case 3 的地址是 00 40 D7 65 ,case 4 的地址是 00 40 D7 72

4f63f526ab133010abe99539ef0324c3.png

        将源程序 case 顺序打乱

        将 case 打乱,再写一个程序

#include int main(){  int n = 123;  switch(n % 5)  {  case 4:    printf("case 4\r\n");    break;  case 0:    printf("case 0\r\n");    break;  default:    printf("default\r\n");  case 3:    printf("case 3\r\n");  case 2:    printf("case 2\r\n");  case 1:    printf("case 1\r\n");    break;  }   return 0;}

        同样方式,在 switch 左括号 { 处按 alt + 8 进入反汇编模式。找到最后一串代码,最后一串数字,仍然是 40D792h

        在地址窗口输入 40D792h,里面的值是 00 40 D7 3C ,00 40 D7 72 ,00 40 D7 65 ,00 40 D7 58 ,00 40 D7 2D 。一共 5 个,后面就没有了。

88652dae6c0b205919cc4fee51fd1ce9.png

        case 4 的地址是 00 40 D7 2D ,case 0 的地址是 00 40 D7 3C

bec8f87806cf8f24986a848b0c6db16c.png

        case 3 的地址是 00 40 D7 58 ,case 2 的地址是 00 40 D7 65 ,case 1 的地址是 00 40 D7 72

807b6d5246829da78f87036c5aa1007f.png

        将其中一个 case 删掉

        将上一个程序的 case3 删掉

#include int main(){  int n = 123;  switch(n % 5)  {  case 4:    printf("case 4\r\n");    break;  case 0:    printf("case 0\r\n");    break;  default:    printf("default\r\n");  case 2:    printf("case 2\r\n");  case 1:    printf("case 1\r\n");    break;  }   return 0;}

        同样方式,在 switch 左括号 { 处按 alt + 8 进入反汇编模式。找到最后一串代码,最后一串数字,是 40D785h

0eea57ae5df1b6121b5d03c8e8df6e65.png

        在地址窗口输入 40D785h,里面的值是 00 40 D7 3C ,00 40 D7 65 ,00 40 D7 58 ,00 40 D7 48 ,00 40 D7 2D 。一共 5 个,后面就没有了。

cf3361cb3db585ee2f60dd18914de53e.png

        case4 的地址是 00 40 D7 2D ,case0 的地址是 00 40 D7 3C ,default 的地址(占用了 case3 的地址)是 00 40 D7 48,case2 的地址是 00 40 D7 58 ,case1 的地址是 00 40 D7 65 

a097ae76925fb3a9ec3ec335831fe855.png

        case 的数据过大

        再写一个程序,最 case 从 case 100 开始

#include int main(){  int n = 123;  switch(n % 5)  {  case 100:    printf("case 4\r\n");    break;  case 101:    printf("case 0\r\n");    break;  default:    printf("default\r\n");  case 104:    printf("case 2\r\n");  case 105:    printf("case 1\r\n");    break;  }   return 0;}

        同样方式,在 switch 左括号 { 处按 alt + 8 进入反汇编模式。找到最后一串代码,最后一串数字,是 40D785h 。        

        在地址窗口输入 40D785h,里面的值是 00 40 D7 36 ,00 40 D7 45 ,00 40 D7 54 ,00 40 D7 54 ,00 40 D7 61 ,00 40 D7 6E 。一共  6 个,后面就没有了。

        如果 case 数据过大,每个 case 数据,减去 case 中最小的数据。这里最小的数据是 100 ,所以都减去 100。仍然是数组的 0 1 2 3 4 5 6,因为没有 case 102 103 ,所以用 default 的地址代替。

        所以 case100 的地址是 00 40 D7 36 ,case101 的地址是 00 40 D7 45 ,default 的地址(占用了 case102 103 的地址)是 00 40 D7 54,case104 的地址是 00 40 D7 61 ,case105 的地址是 00 40 D7 6E

d6417ee53a3c520e72975e5fb3b15053.png

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
这段代码是一个函数,名为ssl_recv_section,接收一个ssl_link_t类型的指针p_link和一个void类型的指针p_buf作为参数,返回一个int32_t类型的值。该函数的作用是接收SSL连接中的数据段。 函数内部首先定义了一个int类型的变量ret,并将其初始化为LINK_RECV_SECTION_RET_FAIL。同时,定义了一个mem_item_t类型的指针变量p_item,将p_buf强制转换为mem_item_t类型后赋值给p_item。再定义一个memory_uint_t类型的指针变量p_data,将p_item->mem.pdata的值赋给p_data。 接下来是一个switch语句,根据p_link->sectionStep的值进行不同的操作。如果为SSL_LINK_RECV_SECTION_START,则将surplusLen赋值为sizeof(record_head_t),从内存池中获取数据并赋值给p_data,将p_data赋值给p_item->mem.pdata,然后将sectionStep赋值为SSL_LINK_RECV_SECTION_HEAD。如果为SSL_LINK_RECV_SECTION_HEAD,则调用函数_recv_head,将p_link和p_data作为参数传递给该函数,并将返回值赋给_ret。如果_ret等于0,则将ret赋值为LINK_RECV_SECTION_RET_ING,并跳出switch语句;如果_ret等于2,则直接跳出switch语句,否则执行case的下一条语句。如果为SSL_LINK_RECV_SECTION_DATA,则调用函数_recv_data,将p_link和p_data作为参数传递给该函数,并将返回值赋给_ret。如果_ret等于0,则将ret赋值为LINK_RECV_SECTION_RET_ING,并跳出switch语句;如果_ret等于1,则将ret赋值为LINK_RECV_SECTION_RET_OK。 最后,如果ret等于LINK_RECV_SECTION_RET_FAIL,则打印出一些调试信息,并将sectionStep赋值为SSL_LINK_RECV_SECTION_START。最终返回ret。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值