最近在看《深入理解计算机系统》,发现汇编挺有趣。

1.条件分支:if语句

下面是一个简单的ifelse函数:

int absdiff(int x, int y)
{if (x < y)return y - x;elsereturn x - y;
}

对这个程序使用如下命令,得到汇编程序,(注意-S选项大写,并且始终用-O1优化选项)

gcc -S ifelse.c -o ifelse.s –O1

可以看到gcc对改程序的翻译与书上略有不同:

pushl   %ebx.cfi_def_cfa_offset 8.cfi_offset 3, -8movl  8(%esp), %ecxmovl   12(%esp), %edxmovl  %edx, %eaxsubl  %ecx, %eaxmovl  %ecx, %ebxsubl  %edx, %ebxcmpl  %edx, %ecxcmovge    %ebx, %eaxpopl  %ebx

gcc中,%ecx: x, %edx:y , %eax: y-x, %ebx: x-y. 比较x与y,若x>=y, %eax: x-y. 最终在%eax中存放result。

其中,cmovge使用了后面将要讲到的 条件传送指令,即先计算一个条件操作的两种结果,然后再根据条件是否满足而选取一个。它要求处理器类型在i686以上,在gcc中可以添加'-march=i686'来编译,但是ubuntu11.10的处理器类型就是i686的(使用uname –p查看),所以上面的编译直接得到采用条件传送指令的汇编代码。

使用条件传送并不总是能改进代码效率,对GCC来说,只有很容易计算时(如只有一条加法指令),它才使用条件传送指令。

【题外话】:

下面的语句产生条件传送的汇编代码:

int arith(int x){return x / 4;
}

使用-O1选项产生汇编代码如下:

   .cfi_startprocmovl  4(%esp), %eax   //get xleal 3(%eax), %edx   //temp = x+3testl %eax, %eax  cmovs   %edx, %eax  //if(x < 0) x = tempsarl    $2, %eax    // return x >> 2ret.cfi_endproc

可以看到,如果是负数,在算术右移时,要加上2^k-1=3的偏置。注意,这里加偏置的原因:一般来说,我们可以直接对补码进行右移操作表示2^k幂,但是真正的除法与补码右移还是有一定区别的:

真正除法一定是舍入到0,所以-2.5得到-2;补码右移则会向下舍入,所以-2.5会得到-3(因为它总是把低位丢弃);

所以,在做真正除法时会加上一个偏置值,(原来CS:APP第65页2.3.7节讲到了这个问题,哎,可惜跳过去了。。)

    int i = -9;cout << i/4 << endl;    //get -2cout << (i>>2) << endl;     //get -3

-9的右移过程如下:得到原码1001——转为补码0111——右移两位1101——转为原码0011,即得到-3。

-9+偏置3过程: -6原码 0110——转为补码1010——右移两位1110——转为原码0010,得到-2.

2.循环

2.1 do-while循环的翻译

汇编中的循环使用 条件测试和跳转 组合起来实现。大部分编译器根据do-while形式产生循环代码,如下求阶乘的循环代码:

int fact_do(int n)
{int result = 1;do{result *= n;n = n-1;}while(n>1);return result;
}

产生汇编如下:

    .cfi_startprocmovl  4(%esp), %edx   //get nmovl $1, %eax        //set result=1
.L2:imull   %edx, %eax      // result *= nsubl $1, %edx        //n--cmpl   $1, %edx        //compare n-1jg .L2             //if(n>1): goto .L2repret.cfi_endproc

2.2 for循环的翻译

// Step1: for循环语句
for(init-expr; test-expr; update-expr)body-statement;// Step2: while循环语句
init-expr;
while(test-expr){body-statement;update-expr;
}// Step3: do-while循环语句
init-expr;
if(!test-expr)goto done
do{body-statement;update-expr;
}while(test-expr);
done:// Step4: goto语句(直观的展示了汇编代码实现)
init-expr;
if(!test-expr)goto done
loop:body-statement;update-expr;if(test-expr)goto loop;
done:

带continue语句时的特例(练习3.24):

i = 0;
while(i < 10){if(i&1)continue;   //continue在i++之前,阻止了i的更新sum += i;i++;
}i = 0;
if(i >= 10)goto done
do{if(i&1)continue; //continue在i++之前,阻止了i的更新sum += i;i++;
}while(i < 10);
done:

do-while循环的continue语句还有一个问题要注意:
翻译为do-while循环时出现了问题,关键是continue的含义是不执行循环体内的内容,直接到达下一个循环点(也就是while处的判断,而不是“do{”处),所以下面语句只会输出1.

int i = 1;
do{printf("%d\n", i);i++;if(i<15)continue;
}while(0);

使用goto语句来保证while循环的更新(写代码时,直接在continue前加一个i++即可):

while(i < 10){if(i&1)goto next;sum += i;
next:i++;
}

3.switch语句

对switch的汇编,GCC会根据开关数量和稀少程度选择是否使用 跳转表 来翻译开关语句。跳转表是一个数组,表项i是代码短的地址,其执行时间与开关情况的数量无关。如下switch语句:

int switch_eg(int x, int n){int result = x;switch(n){case 100:result *= 13;break;case 102:result += 10;case 103:result += 11;break;case 104:case 106:result *= result;break;default:result = 0;}return result;
}

使用-O1翻译成汇编为:

   .cfi_startprocmovl  4(%esp), %eaxmovl   8(%esp), %edxsubl   $100, %edxcmpl  $6, %edxja  .L8jmp  *.L7(,%edx,4).section   .rodata.align 4.align 4
.L7:.long   .L3.long    .L8       //case 101: default.long  .L4.long    .L5.long    .L6.long    .L8       //case 105: default.long  .L6.text
.L3:                                     //case 100: result *= 13leal  (%eax,%eax,2), %edx   // get 3*xleal    (%eax,%edx,4), %eax   //get x+4*(3x)= 13*xret
.L4:                                     //case 102: result += 10addl $10, %eax
.L5:                                     //case 103: result += 11addl $11, %eaxret
.L6:                                     //case 104/106: result *= resultimull %eax, %eaxret
.L8:                                     //default: result = 0movl $0, %eaxret.cfi_endproc

转载于:https://www.cnblogs.com/dandingyy/archive/2013/01/03/2837053.html

C语言控制流对应的汇编语句相关推荐

  1. 单片机c语言中的循环语句,单片机c语言教程:C51循环语句

    循环语句是几乎每个程序都会用到的,它的作用就是用来实现需要反复进行多次的操 作.如一个 12M 的 51 芯片应用电路中要求实现 1 毫秒的延时,那么就要执行 1000 次空语句 才能达到延时的目的( ...

  2. GCC在C语言中内嵌汇编 asm __volatile__

    在内嵌汇编中,可以将C语言表达式指定为汇编指令的操作数,而且不用去管如何将C语言表达式的值读入哪个寄存器,以及如何将计算结果写回C 变量,你只要告诉程序中C语言表达式与汇编指令操作数之间的对应关系即可 ...

  3. 汇编和c语言混合编程缺点,汇编C语言混合编程经验总结

    ARM汇编语言和C语言混合编程 ATPCS规灾了一种模块化设计的观念,其基本内容是C模块(函数)和汇编模块(函数)相互的一套规?中还有类似的一套规晕腋芯谙呋惚喙δ芮看用有很多忌讳),厅知道(很寄几它/ ...

  4. C语言进阶——内联汇编

    内联函数 在 C 语言中,我们可以指定编译器将一个函数代码直接复制到调用其代码的地方执行.这种函数调用方式和默认压栈调用方式不同,我们称这种函数为内联函数.有点像宏. 优点:内联函数降低了函数的调用开 ...

  5. c语言while else语句用法,C语言注释C语言的主要功能else语句,while语句,dowhile语句,sw...

    C语言注释C语言的主要功能else语句,while语句,dowhile语句,switch语句和for语句C语言允许直接访问物理地址可以执行位操作可以实现汇编语言的大部分功能,可以直接对硬件目标生成的代 ...

  6. C语言char*s 4,求讲解几道C语言的题 52 声明语句为“char s[4][15],*p1,**p2;int x,*y;”,下列语句中正...

    求讲解几道C语言的题 52 声明语句为"char s[4][15],*p1,**p2:int x,*y:",下列语句中正 2018-6-15来自ip:14.144.15.70的网友 ...

  7. R语言If、Else条件语句实战

    R语言If.Else条件语句实战 目录 R语言If.Else条件语句实战 # if()函数和else() 函数 # ifelse函数

  8. Win32 汇编语句模板

    Win32 汇编语句模板 一 变量 ;句柄 hInstance dd 0 hWnd dd 0 hPen dd 0 hPend dd 0 oldPen dd 0;过程变量 hInst :DWORD hP ...

  9. 浅析VS2010反汇编 VS 反汇编方法及常用汇编指令介绍 VS2015使用技巧 调试-反汇编 查看C语言代码对应的汇编代码...

    浅析VS2010反汇编 2015年07月25日 21:53:11 阅读数:4374 第一篇 1. 如何进行反汇编 在调试的环境下,我们可以很方便地通过反汇编窗口查看程序生成的反汇编信息.如下图所示. ...

最新文章

  1. vijos 1512 SuperBrother打鼹鼠
  2. 2021年度人工智能产品TOP10,百度飞桨EasyDL再获业界认可
  3. java核心技术----访问权限
  4. Ubuntu系统---以virtualenv方式安装Tensorflow-CPU
  5. 斗地主AI算法——第十章の被动出牌(4)
  6. ue4移动到一定距离_UE4移动组件详解(一)——移动框架与实现原理
  7. 轨迹压缩文献阅读 TrajStore: An Adaptive Storage System for Very Large Trajectory Data Sets
  8. 【java学习之路】(java框架)008.JdbcTemplate
  9. 【转】IE6 浏览器常见兼容问题 大汇总(23个)
  10. JDBC批处理---(java 对数据库的回滚) .
  11. python中的主函数调用_调用主函数中的函数
  12. 【echarts】柱状图上方显示数值
  13. Excel 复制粘贴筛选出来的数据行
  14. 30多个投资理财工具,总有一款适合你
  15. Exception: ROM is missing for xxxx, see https://github.com/openai/atari-py 强化学习安装Atari环境时ROMS丢失解决办法
  16. 微商史上最全软文标题写作套路(收藏版)
  17. 一次买房子血淋淋的教训
  18. 关于系统分析师的考试感想
  19. 阿里云弹性云桌面安装失败问题解决记录(.net framework 4.6.2 or later:Error Code: 12029)
  20. 详细讲解二极管的钳位电路和限幅电路

热门文章

  1. 顶级程序员和普通程序员在思维模式上的5个区别!
  2. mysql kill_Mysql使用kill命令解决死锁问题(杀死某条正在执行的sql语句)
  3. python中字典和集合对象是无序的_Python基础(四):元组、字典和集合
  4. python 比例之差z假设检验_假设检验在数据分析中的应用
  5. 燕山大学计算机专业研究生怎么样,求助大家!重庆邮电大学计算机专业的研究生值得一读吗?...
  6. csv导入mysql linux_如何将CSV文件导入MySQL表
  7. java数据类型及其说明
  8. ux设计_UX设计101:
  9. 软件项目开发 学校自行开发_自行开发游戏
  10. 小程序 显示细线_精心设计:高密度显示器上的细线