[++i_与i++]一道简单的题目引发的思考

  • 格式:doc
  • 大小:5.13 MB
  • 文档页数:11

下载文档原格式

  / 11
  1. 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
  2. 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
  3. 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。

一道简单的题目引发的思考

++i 与i++

——Don't believe in magic !Understand what your program do ,how they do .引言

昨晚一时兴起,我脑子就问自己下面的代码会输出什么,也不知道我脑子为什么有这个代码模型,只是模糊的有些印象:

01#include

02#include

03

04int main(int argc,char** argv)

05{

06int i=3,j;

07j=(i++)+(i++)+(++i);

08printf("i = %d, j = %d\n",i,j);

09exit(0);

10}

您会怎样考虑这个问题呢?您不运行这个程序能准确地说出答案吗?我猜想肯定有大部分人不能肯定且准确地说出答案!如果您不能,这篇文章就是为你准备的,保证您看完之后豁然开朗!请细看下文,outline如下:

1、诸君的回答

我那这道题目问了几个人,他们的答案不尽相同。

1.1、A君的回答

因为i = 3,故依次i++=4,i++=5,++i=6,i最后输出为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;但是前面两个++都是后置++,故先做i+i+(++i)操作,然后才在i++,i++操作,第三个++是前置++,故等价于i+i +(++i)=3+3+4=10,i++,i++。

到底哪个人说得对呢?

2、编译器的输出

首先让我们先来看看编译器会输出什么?

2.1、Visual Studio的输出

运行环境:Win7+VS2005 or VS2010,输出如下图所示:

2.2、GCC的输出

运行环境:Ubuntu 10.04+gcc (Ubuntu 4.4.3-4ubuntu5) 4.4.3,运行结果如下:

2.3、Visual C++的输出

运行环境:Win7+VC2010,输出和VS一样,及i = 6 & j = 12

看到这里你肯定想问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编译之后的执行文件进行反编译分析验证正确性。在Linux下面可以用objdum p –d xxx(执行文件)命令反汇编执行文件。反编译之后可以看到如下图所示的代码:

说明:Linux下采用的是AT&T的汇编语法格式,Windows下面采用的是Intel汇编语法格式。二者的主要区别在于:

1.指令操作数的赋值方向是不同的

Intel:第一个是目的操作数,第二个是源操作数

AT&T:第一个是源操作数,第二个是目的操作数

2.指令前缀

AT&T:寄存器前边要加上%,立即数前要加上$

Intel:没有这方面的要求

3.内存单元操作数

Intel:基地址使用[]

AT&T:基地址使用()

比如:intel中mov ax,[bx]

AT&T中movl (%eax),%ebx

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.AT&T中跳转指令标号后的后缀表示跳转方向,“f”表示向前,“b”表示向后下面我们重点分析红框中的代码:

movl $0x3 ,0x1c(%esp):将3赋给i,即i=3

mov 0x1c(%esp) ,%eax:将esp中的i放到eax中

add %eax ,%eax:进行i+i操作,即3+3

addl $0x1 ,0x1c(%esp):对i进行加1操作,即表达式中的(++i)

add 0x1c(%esp),%eax:将eax中i+i的结果6,加上++i之后的i,即6+4=10 addl $0x1 ,0x1c(%esp):对i进行加1操作,即表达式中的(i++)

addl $0x1 ,0x1c(%esp):对i进行加1操作,即表达式中的(i++)

至此关键代码已经分析完成,由此可见我们之前对gcc编译器上的分析是正确的。