版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领
文档简介
1、-一道简单的题目引发的思考+i 与i+Don't believe in magic !Understand what your program do ,how they do .引言昨晚一时兴起,我脑子就问自己下面的代码会输出什么,也不知道我脑子为什么有这个代码模型,只是模糊的有些印象:01*include <stdio.h> 02*include <stdlib.h> 03 04intmain(intargc,char* argv) 0506 inti=3,j; 07 &
2、#160; j=(i+)+(i+)+(+i); 08 printf("i = %d, j = %dn",i,j); 09 e*it(0); 10您会怎样考虑这个问题呢.您不运行这个程序能准确地说出答案吗.我猜想肯定有大局部人不能肯定且准确地说出答案!如果您不能,这篇文章就是为你准备的,保证您看完之后豁然开朗!请细看下文,outline如下:1、诸君的答复我那这道题目问了几个人,他们的答案不尽一样。1.1、A君的答复因为i = 3,故依次i+=4,i+=5,+i=6,i最后输
3、出为i = 6;但是由于前面两个+是后置+,最后一个+是前置+,故j = 3+4+6 = 13。1.2、B君的答复因为i = 3,故第一个i+后为4,第二个i+后为5,接着做i+i操作 = 5+5=10,最后与(+i)相加 = 10+6=16。1.3、C君的答复因为i = 3,故依次i+=4,i+=5,+i=6,i最后输出为i = 6;但是第一i、第二个i的+是后置+,先进展i+i操作,然后进展两次i+后置操作,故等价于(i)+(i) = 3+3=6,i+,i+,最后与+i=6相加等于12。1.4、D君的答复因为i = 3,故依次i+=4,i+=5,+i=6,i最后输出为i = 6;但是前面两
4、个+都是后置+,故先做i+i+(+i)操作,然后才在i+,i+操作,第三个+是前置+,故等价于 i+i+(+i)=3+3+4=10,i+,i+。到底哪个人说得对呢.2、编译器的输出首先让我们先来看看编译器会输出什么?2.1、Visual Studio的输出运行环境:Win7+VS2005 or VS2021,输出如以下图所示:2.2、GCC的输出运行环境:Ubuntu 10.04+gcc (Ubuntu 4.4.3-4ubuntu5) 4.4.3,运行结果如下:2.3、Visual C+的输出运行环境:Win7+VC2021,输出和VS一样,及i = 6 & j = 12看到这里你肯定
5、想问why? why? why?3、分析重编译器的输出结果来看貌似C君、D君的分析都是对的,这种差异跟编译器有直接的关系,因为对于这个表达式怎么编译还没有形成标准,编译器的结合方向不同,答案因此会有所不同。而且当然还包括运算符的优先级等。其实顶多算C君答对了一局部,其他几个人的答复都是错的,详情见下面的分析。3.1、gcc编译器上的分析(i+)+(i+)+(+i) <=> i+i+(+i); i+; i+;即如果表达式中含有i+,一律替换成i,然后在表达式之后进展i+操作。这样的话上面的代码就可以很好的理解了,即3+3+4=10。3.2、分析gcc编译之后的汇编代码可以对gcc编译
6、之后的执行文件进展反编译分析验证正确性。在Linu*下面可以用objdump d *(执行文件)命令反汇编执行文件。反编译之后可以看到如以下图所示的代码:说明:Linu*下采用的是ATT的汇编语法格式,Windows下面采用的是Intel汇编语法格式。二者的主要区别在于:1. 指令操作数的赋值方向是不同的 Intel:第一个是目的操作数,第二个是源操作数 ATT:第一个是源操作数,第二个是目的操作数2. 指令前缀 ATT:存放器前边要加上,立即数前要加上$ Intel:没有这方面的要求3. 存单元操作数 Intel:基地址使用 AT&T:基地址使用比方:intel中 mov a*,b*
7、 AT&T中 movl (%ea*),%eb*4. 操作码的后缀 AT&T中操作码后面有一个后缀字母:“l 32位,“w 16位,“b 8位 Intel却使用了在操作数前面加dword ptr, word ptr, byte ptr的格式例如:mov al,bl (Intel) movb %bl %al (AT&T)5. ATT中跳转指令标号后的后缀表示跳转方向,“f表示向前,“b表示向后下面我们重点分析红框中的代码:movl $0*3 ,0*1c(%esp):将3赋给i,即i=3 mov 0*1c(%esp) ,%ea*:将esp中的i放到ea*中 add %ea*
8、,%ea*:进展i+i操作,即3+3 addl $0*1 ,0*1c(%esp):对i进展加1操作,即表达式中的(+i) add 0*1c(%esp),%ea*:将ea*中i+i的结果6,加上+i之后的i,即6+4=10 addl $0*1 ,0*1c(%esp):对i进展加1操作,即表达式中的(i+) addl $0*1 ,0*1c(%esp):对i进展加1操作,即表达式中的(i+)至此关键代码已经分析完成,由此可见我们之前对gcc编译器上的分析是正确的。3.3、vs编译器上的分析(i+)+(i+)+(+i) <=>(+i)+i+i; i+; i+;即如果表达式中含有前置+i,首
9、先执行+i操作;表达式中的i+,一律换成i,然后执行加法操作;最后在进展i+操作。这样的话上面的代码就可以很好的理解而来,即首先执行+i,i变为4了;然后进展i+i+i=4+4+4;i+,i+。其实对于VS/VC2021编译器中的可以总结为:当用于四则运算时,前置+/-的运算优先级最高,后置+/-的运算优先级最小,其它的居中。跟你书上看到是不是不同!3.4、分析VS编译之后的汇编代码用W32Dasm反汇编vs编译生成的e*e文件,追踪代码。我们可以看到如以下图所示的代码:下面重点分析一下框中代码:mov ebp-08,3:将3赋给i,即i=3 mov ea*,dword ptr ebp-08:
10、将ebp中的i的值放到ea*中,是"累加器"(accumulator), 它是很多加法乘法指令的缺省存放器。dword ptr表示这是一个双字指针,即所要寻址的数据是一个双字4字节 add ea*,1:对ea*中的i进展加1操作mov dword ptr ebp-08 ,ea*:将ea*中的i赋给ebp中i,即将i加1之后的值赋给i,也即到达i=i+1的效果 mov ec*,dword ptr ebp-08:将ebp中的i放到ec*中 add ec*,dword ptr ebp-08:将ebp中的值加上i,即4+4 add ec*,dword ptr ebp-08:将ebp
11、中的值加上i,即4+4+4 mov dword ptr ebp-14,ec*:将ec*中的值赋给j mov ed*,dword ptr ebp-08:将i放到ed*中 add ed*,1:对ed*中的i进展加1操作 mov dword ptr ebp-08 ,ed*:将ed*中的i赋给ebp中i,即将i加1之后的值赋给i,也即到达i=i+1的效果 mov ea*,dword ptr ebp-08:将i放到ea*中 add ea*,1:对ea*中的i进展加1操作 mov dword ptr ebp-08 ,ea*:将ea*中的i赋给ebp中i,即将i加1之后的值赋给i,也即到达i=i+1的效果至
12、此,上面表达式的关键运算局部已经分析完成。从这里可以知道,上面我们地VS编译器的分析是正确的。4、发散思维可以说通过上面则篇幅的介绍,我们对涉及前置+和后置+的加法运算表达式的计算过程有了一个清楚的认识,下面就我们发散一下我们的思维,释放我们的能量。4.1、思维放射您看下面的代码会输出什么,现在知道了吧!view sourceprint?01*include <stdio.h> 02*include <stdlib.h> 03 04intmain(intargc,char* argv) 0506 int
13、i=3,j=3,k=3,l=3,m=3,n=3,result1,result2,result3,result4,result5,result6; 07 result1=(+i)+(+i); 08 printf("i = 3n"); 09 printf("result1= (+i)+(+i) = %dnn",result1); 10 11 result2
14、=(j+)+(j+); 12 printf("j = 3n"); 13 printf("result2= (j+)+(j+) = %dnn",result2); 14 15 result3=(+k)+(+k)+(+k); 16 printf("k = 3n"); 17
15、0; printf("result3= (+k)+(+k)+(+k) = %dnn",result3); 18 19 result4=(+l)+(+l)+(l+); 20 printf("l = 3n"); 21 printf("result4= (+l)+(+l)+(l+) = %dnn",result4); 22 23
16、 result5=(m+)+(m+)+(m+); 24 printf("m = 3n"); 25 printf("result5=(m+)+(m+)+(m+) = %dnn",result5); 26 27 result6=(n+)+(+n)+(n+); 28 printf("n = 3n"); 2
17、9 printf("result6=(n+)+(+n)+(n+) = %dnn",result6); 30 e*it(0); 31请不看结果先自己分析一下,然后和结果比照!4.2、VS的输出运行环境:Win7+VS2005 or VS2021,输出如以下图所示:4.3、GCC的输出运行环境:Ubuntu 10.04+gcc (Ubuntu 4.4.3-4ubuntu5) 4.4.3,运行结果如下:根据前面我们挖掘到的规则,我们可以得到result3之外所有其它答案。最后,还有一点要说明的是:gcc中的加法运算表达死中,是按照从左到右按顺序,如果运算符两边有+i操作数,就先进展+i操作,然后进展加法运算;vs中的加法运算表达式中,则不
温馨提示
- 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
- 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
- 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
- 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
- 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
- 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
- 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。
最新文档
- 办公家具租赁合同
- 话费返还活动方案
- 诗词系列活动方案
- 采购合同对策制度
- 设计跳绳艺术活动方案
- 紫外线防护美白日霜行业跨境出海项目商业计划书
- 档案管理标准流程
- 设计公司揽活活动方案
- 菏泽冬季体验活动方案
- 融合户外拓展活动方案
- 《肠造口术后并发症护理研究进展综述》7400字
- 《受限空间施工方案》
- 高层旧楼外墙翻新修饰施工方案
- 第四章第五节蛋白质运转
- 医学伦理审查与医院伦理委员会建设
- FANUC系统数控铣床几个简单编程实例
- 籍贯对照表完整版
- 中国铸造产业地图
- GB/T 2677.10-1995造纸原料综纤维素含量的测定
- GB/T 18268.1-2010测量、控制和实验室用的电设备电磁兼容性要求第1部分:通用要求
- 第3章(2) VFP的常用函数
评论
0/150
提交评论