C语言控制流对应的汇编语句

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

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

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/296108.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

C# 显式接口成员实现

如果类实现两个接口&#xff0c;并且这两个接口包含具有相同签名的成员&#xff0c;那么在类中实现该成员将导致两个接口都使用该成员作为它们的实现。然而&#xff0c;如果两个接口成员实现不同的功能&#xff0c;则可能会导致其中一个接口的实现不正确或两个接口的实现都不正…

oracle 加密怎么解密,oracle加密encrypt,解密decrypt,

oracle加密encrypt,解密decrypt&#xff0c;目录oracle加密encrypt,解密decrypt加密解密oracle加密encrypt,解密decrypt有的oracle版本没有加解密函数&#xff0c;以下操作可以手动添加oracle数据使用加密解密&#xff0c;我们首先要先赋予dbms_crypto权限给用户。grant execut…

C语言可变参数宏

岁月不饶人&#xff0c;这年纪大了记忆力真是差了很多。今天写程序需要用到可变参数的宏&#xff0c;可是忘了怎么写&#xff0c;这倒也没什么&#xff0c;因为我一向是不记忆这些语法细节的&#xff0c;反正我知道程序里有个地方用到了这种技巧&#xff0c;但是可悲的是我忘了…

没有数学天赋是一种什么体验?

全世界只有3.14 % 的人关注了爆炸吧知识虽然我不懂但我热爱数学♪没有数学天赋是一种什么体验&#xff1f;1 看不懂懵逼矩阵2 小学数学不及格&#xff0c;找了个纹身店&#xff0c;问老板纹个九九乘法口诀多少钱。老板说了一个价钱&#xff0c;后来因算不出要存多少天早餐钱而作…

20151026c#2

变量是内存里存储的&#xff0c;运行在cpu里的。 数据类型 值类型&#xff1a;所有的数值类型都是值类型&#xff08;short int long float double、bool)、枚举、结构 引用类型&#xff1a;对象、字符串、object、数组 区别&#xff1a; 1、值类型 class Program{static void …

aix 安装oracle9,IBM P570 小型机AIX5.3系统安装ORACLE9i

IBM P570小型机AIX5.3系统安装ORACLE9i(图略)数据库系统安装概叙本文写在Oracle安装完后&#xff0c;根据实际安装情况写的操作小节。详细叙述了安装过程中的每一个步骤。硬件系统为IBM P570小型机&#xff0c;配两块硬盘&#xff1b;阵列为IBM DS43002T7块硬盘&#xff0c;每块…

beautiful sentences

The most splendid achievement of all is the constant striving to surpass yourself and to be worthy of your own approval. This is how happiness blooms.人生最大的成就是不断的超越自己&#xff0c;并无愧于自己的内心。这是幸福的源泉。So don’t wait for someone t…

数字化如何界定IT与OT?

信息技术蓬勃发展并与制造业加速融合&#xff0c;拉开数字化转型大幕。而在工业领域中&#xff0c;IT&#xff08;Information Technology&#xff09;信息技术与OT&#xff08;Operation Technology &#xff09;操作技术之间天然存在着种种差异&#xff0c;两者都有各自的目标…

java宝典

说明&#xff0c;为了减轻大家的负担和节省大家的时间&#xff0c;一些过时知识点和被笔试概率极低的题目不再被收录和分析。 回答问题的思路&#xff1a;先正面叙述一些基本的核心知识&#xff0c;然后描述一些特殊的东西&#xff0c;最后再来一些锦上添花的东西。要注意有些…

Android手机通过电脑上网的几种方法

本文定位&#xff1a;没有wifi&#xff0c;想让手机通过电脑的有线连接上网。 android手机可以通过wifi上网&#xff0c;这个很好。可当没有wifi的时候怎么上啊&#xff0c;3G--->流量受限&#xff0c;看视频很不划算&#xff01;我这里总结了几种方法供大家参考。 方法一&a…

一个常见的物理现象,直今还是未解之谜!

全世界只有3.14 % 的人关注了爆炸吧知识在我们的日常生活中存在着很多有趣的物理现象&#xff0c; 科学家对这些现象进行深入研究后&#xff0c;可以通过这些现象延伸&#xff0c;从而在前沿科技发展上得到很大的帮助&#xff0c;甚至去解决宇宙中出现的难题。但是在日常生活中…

SegmentFault 创始人祁宁对话 C# 之父 Anders Hejlsberg

导读 上周&#xff0c;C#、Delphi 之父 Anders Hejlsberg 亲临帝都&#xff0c;就 TypeScript 做了一场技术分享&#xff0c;并与众多开发者就此进行了技术探讨。Anders Hejlsberg 加入微软的 19 年里&#xff0c;一直致力于 C# 和 .NET 的研发工作。同时&#xff0c;作为 Type…

c 中oracle连接字符串,Oracle连接字符串C#

Netty入门学习一.他山之石 Netty实现原理浅析 http://www.importnew.com/15656.html netty线程模型 http://www.infoq.com/cn/articles/ne ...史上最全的Win8快捷键大全下列的 Win8 快捷键列表汇总均收集自网络,未全部实测,也有可能有Win7时代的热键混迹其中,不管怎样,如有错漏…

如何验证某个 string 是否为合法的 GUID ?

咨询区 001&#xff1a;假如有一个string字符串&#xff0c;请问如何判断它是合法的GUID还是一个普通的数字型字符串, 是否可以用包含 字母 进行区分&#xff1f;回答区 Can Gencer&#xff1a;判断是否有 字母 的方式是不靠谱的&#xff0c;比如说&#xff1a;FFFFFFFF-FFFF-F…

JS:1.3,函数(function)

ylbtech-JS&#xff1a;函数-导航函数定义返回函数调用一个函数调用一个函数(带参数)返回值的函数调用外部的js文件JS&#xff1a;3.1&#xff0c;函数(function)-定义 返回顶部1&#xff0c;定义函数语法 通过定义函数名称&#xff0c;参数和代码语句来创建函数。function 函数…

美女,你这是把腰带当裙子了?

1 你这是把腰带当裙子了&#xff1f;&#xff08;良心建议&#xff1a;穿这裙子一定要专心...&#xff09;▼2 真正充满味道的食堂&#xff01;▼3 &#xff1f;&#xff1f;&#xff1f;这又是一只有故事的猫▼4 女生的肚子可以多神奇&#xff1f;&#xff08;dy&#xff…

UITableView定制accessoryView出现的连带问题

为了美化UI,想实现如下图的效果:点击高亮 出发点是好的。没想到&#xff0c;出现了下图的连带问题:选择一行的时候&#xff0c;竟然连带的出现了高亮效果 这个如何是好&#xff1f;经过网络搜索&#xff0c;发现我不是第一个遇到这样的问题&#xff1a;custom-accessory-button…

java面试笔试大汇总

http://java.chinaitlab.com/base/724898.html 1.抽象&#xff1a;  抽象就是忽略一个主题中与当前目标无关的那些方面&#xff0c;以便更充分地注意与当前目标有关的方面。抽象并不打算了解全部问题&#xff0c;而只是选择其中的一部分&#xff0c;暂时不用部分细节。抽象包括…

判断三点是顺时针还是逆时针方向

判断三点是顺时针还是逆时针方向设 p1(x1,y1)&#xff0c; p2(x2,y2)&#xff0c; p3(x3,y3) 求向量 p12(x2-x1,y2-y1) p23(x3-x2,y3-y2) 则当 p12 与 p23 的叉乘&#xff08;向量积&#xff09; p12 x p23 (x2-x1)*(y3-y2)-(y2-y1)*(x3-x2) 为正时&#xff0c;p1-p2-p3 路径的…

linux编写arm执行文件夹,嵌入式ARM-Linux平台上的编译、配置和运行使用

本文介绍了嵌入式ARM-Linux上的常用应用程序wpa_supplicant(以及wpa_supplicant依赖的libnl和openssl)的编译、配置和运行使用&#xff0c;iw、hostapd等应用的编译和使用。wpa_supplicant 编译和配置运行从https://w1.fi/wpa_supplicant/ (git地址git://w1.fi/hostap.git)下载…