`
javatgo
  • 浏览: 1169300 次
  • 性别: Icon_minigender_2
  • 来自: 北京
文章分类
社区版块
存档分类
最新评论

Inside i++

阅读更多


i++、++i、i=i+1、效率怎么样?看过一本书上说,i++比i=i+1好
的地方是因为i=i+1中的那个1要占用一个寄存器,所以速度没有
i++快,于是我想验证一下。另外,以前听说过Java中的“i=i++”
得不到正确结论,也就是应该是“先累加再赋值”,但Java经过这
种运算后,i值居然没有变化。所以在这里,想一并把这几个问题
在C中验证一下。


=====================测试的C源程序====================

#01: #include <stdio.h>
#02:
#03: main()
#04: {
#05: int i=0, j=0;
#06: i=i++;
#07:
#08: i=i+1;
#09: i++;
#10: ++i;
#11:
#12: j=i+1;
#13: j=i++;
#14: j=++i;
#15:
#16: printf("i=%d", i);
#17: }

======================================================

下面是我在 VC++ 6.0 + SP5 / Window 2000环境下对上述源程序的反汇编:

5: int i=0, j=0;
0040D698 mov dword ptr [ebp-4],0
0040D69F mov dword ptr [ebp-8],0

6: i=i++;
0040D6A6 mov eax,dword ptr [ebp-4]
0040D6A9 mov dword ptr [ebp-4],eax
0040D6AC mov ecx,dword ptr [ebp-4]
0040D6AF add ecx,1
0040D6B2 mov dword ptr [ebp-4],ecx

8: i=i+1;
0040D6B5 mov edx,dword ptr [ebp-4]
0040D6B8 add edx,1
0040D6BB mov dword ptr [ebp-4],edx

9: i++;
0040D6BE mov eax,dword ptr [ebp-4]
0040D6C1 add eax,1
0040D6C4 mov dword ptr [ebp-4],eax

10: ++i;
0040D6C7 mov ecx,dword ptr [ebp-4]
0040D6CA add ecx,1
0040D6CD mov dword ptr [ebp-4],ecx

12: j=i+1;
0040D6D0 mov edx,dword ptr [ebp-4]
0040D6D3 add edx,1
0040D6D6 mov dword ptr [ebp-8],edx

13: j=i++;
0040D6D9 mov eax,dword ptr [ebp-4]
0040D6DC mov dword ptr [ebp-8],eax
0040D6DF mov ecx,dword ptr [ebp-4]
0040D6E2 add ecx,1
0040D6E5 mov dword ptr [ebp-4],ecx

14: j=++i;
0040D6E8 mov edx,dword ptr [ebp-4]
0040D6EB add edx,1
0040D6EE mov dword ptr [ebp-4],edx
0040D6F1 mov eax,dword ptr [ebp-4]
0040D6F4 mov dword ptr [ebp-8],eax


=================================================================

下面是我在 SCO UNIX下用cc -g 对上述源程序编译后,用dbx打出的内存汇编代码:

( int i=0, j=0; )
0x0000015e (main:5) mov DWord Ptr [ebp-0x04],$0
0x00000165 (main:5) mov DWord Ptr [ebp-0x08],$0

( i=i++; )
0x0000016c (main:6) mov eax,DWord Ptr [ebp-0x04]
0x0000016f (main:6) inc DWord Ptr [ebp-0x04]
0x00000172 (main:6) mov DWord Ptr [ebp-0x04],eax

( i=i+1; )
0x00000175 (main:8) inc DWord Ptr [ebp-0x04]

( i++; )
0x00000178 (main:9) inc DWord Ptr [ebp-0x04]

( ++i; )
0x0000017b (main:10) inc DWord Ptr [ebp-0x04]

( j=i+1; )
0x0000017e (main:12) mov eax,DWord Ptr [ebp-0x04]
0x00000181 (main:12) inc eax
0x00000182 (main:12) mov DWord Ptr [ebp-0x08],eax

( j=i++; )
0x00000185 (main:13) mov eax,DWord Ptr [ebp-0x04]
0x00000188 (main:13) inc DWord Ptr [ebp-0x04]
0x0000018b (main:13) mov DWord Ptr [ebp-0x08],eax

( j=++i; )
0x0000018e (main:14) inc DWord Ptr [ebp-0x04]
0x00000191 (main:14) mov eax,DWord Ptr [ebp-0x04]
0x00000194 (main:14) mov DWord Ptr [ebp-0x08],eax

======================================================================

1、从上述的汇编代码我们不难看出对于i=i+1; i++; ++i这三个指令的汇编
指令无论是在VC下还是在SCO UNIX的cc下都是一样的(虽然这两个编译器
对其汇编得到的指令不太一样,但是它们对这三条指令的汇编都是一样的,
这里,我是关闭编译器优化的选项,也许现在的编译器自动对之优化了)

在VC下都是:
0040D6B5 mov edx,dword ptr [ebp-4]
0040D6B8 add edx,1
0040D6BB mov dword ptr [ebp-4],edx

在cc下都是:
0x0000017b (main:10) inc DWord Ptr [ebp-0x04]

2、对于复合指令 j=i+1; j=i++; 却有所不同,
在VC下:j=i+1 是三条指令,而 j=i++ 却有五条指令,这也很合理。
在SCO下: j=i+1 和 j=i++ 都是三条指令。(j=i++指令数比VC少)

3、对于i=i++,在VC下可以得到正确的结果 i=1;而在SCO下却是i=0; 这可以
从其汇编看到。

VC对i=i++的汇编是:

0040D6A6 mov eax,dword ptr [ebp-4] //取内存值i到eax
0040D6A9 mov dword ptr [ebp-4],eax //把eax值放到内存i中
0040D6AC mov ecx,dword ptr [ebp-4] //取内存值i到ecx
0040D6AF add ecx,1 //寄存器ecx加1
0040D6B2 mov dword ptr [ebp-4],ecx //把ecx值放到内存i中

SCO对i=i++的汇编是:

//取内存i到寄存器eax中
0x0000016c (main:6) mov eax,DWord Ptr [ebp-0x04]

//对内存i进行累加
0x0000016f (main:6) inc DWord Ptr [ebp-0x04]

//把寄存器eax的值放到内存i中
0x00000172 (main:6) mov DWord Ptr [ebp-0x04],eax

可见,之所以SCO得不到正确的结果的原因了。我已为其加上了注释,相信各位是看得懂的。

【结论】:
1、如果是单语句,无论是i=i+1; i++; ++i;其效率是完全一样的。
2、之所以SCO下的i=i++得不到正确结果,是因为其编译器追求效率的结果。(Java亦如此)
3、观察SCO下的汇编指令“inc DWord Ptr [ebp-0x04]”,难道可以直接操作内存吗?(拿不准)
4、各个产商的编译器各有不同,所产生的语句的指令代码也有所不同,C++就更尤其如此啦。

======================================================================

最后附上Java对i=i++的测试

源程序:

#1: class Test
#2: {
#3: public static void main(String[] argv)
#4: {
#5:int i=0;
#6:i=i++;
#7:System.out.println("i="+i);
#8:}
#9: }


用javac -g Test.java 把其编译成 Test.class。
再用javap -c Test打出其虚拟机指令代码如下:

D:\>javap -c Test

Compiled from Test.java
class Test extends java.lang.Object {
Test();
public static void main(java.lang.String[]);
}

Method Test()
0 aload_0
1 invokespecial #1 <Method java.lang.Object()>
4 return

Method void main(java.lang.String[])
0 iconst_0
1 istore_1
2 iload_1
3 iinc 1 1
6 istore_1
7 getstatic #2 <Field java.io.PrintStream out>
10 new #3 <Class java.lang.StringBuffer>
13 dup
14 invokespecial #4 <Method java.lang.StringBuffer()>
17 ldc #5 <String "i=">
19 invokevirtual #6 <Method java.lang.StringBuffer append(java.lang.String)>
22 iload_1
23 invokevirtual #7 <Method java.lang.StringBuffer append(int)>
26 invokevirtual #8 <Method java.lang.String toString()>
29 invokevirtual #9 <Method void println(java.lang.String)>
32 return

可见其中的:
1 istore_1
2 iload_1
3 iinc 1 1
6 istore_1
就是i=i++的虚拟机指令,想来一定和SCO的编译器有异曲同工之处。

【备注】
这段程序的结果是i=0,我是在 j2se 1.4.0 下进行的测试。
虽然说,这是一个BUG,但是有多少人又会写i=i++这种无聊的语句呢?
不过却可以了解一下编译器(解释器)的工作方式。

——
(版权所有,如需转载,请注明作者和出处)

分享到:
评论

相关推荐

    Inside COM+ Code.rar

    描述中的"it's been over a decade since I started writing applications for Microsoft Windows"暗示了作者有丰富的Windows平台开发经验,因此这个压缩包中的内容可能是作者多年实践和理解的结晶,涵盖了从基础的...

    I-BUS Inside Inside the BMW Cars entertainement Serial Bu

    标题“Inside the BMW Cars entertainement Serial Bu”可能是指I-BUS系统的内部结构和工作原理。I-BUS(Inter Equipment Bus)是一种用于汽车内部通信的总线系统,用于连接汽车内的多个电子控制单元(ECUs)以及...

    i2c-rk3x.rar_inside

    标题 "i2c-rk3x.rar_inside" 暗示了我们正在处理与RK3x系列处理器相关的I2C(Inter-Integrated Circuit)通信协议的实现,其中".rar_inside"可能表示这是一个压缩包中的文件,包含了对RK3x芯片I2C功能的详细配置或...

    Inside Smalltalk I.pdf

    根据提供的文件信息,我们可以推断出这是一本关于Smalltalk编程语言的书籍,书名为《Inside Smalltalk Volume I》。本书由W. LaLonde与J.R. Pugh合著,作者均来自卡尔顿大学计算机科学学院。此书旨在介绍Smalltalk的...

    Inside the native api

    《Inside the Native API》是关于Windows NT操作系统内核函数的权威文档,对于深入理解Windows系统底层运作机制,尤其是驱动程序和内核级开发而言,它是一份不可或缺的参考资料。这份文档详细介绍了原生API(Native ...

    Inside the Machine

    6. **总线与I/O子系统**:书中还探讨了计算机内部的数据传输机制,包括不同类型的总线和I/O接口。 7. **高级主题**:此外,《Inside the Machine》还涉及了一些更高级的话题,如虚拟化技术、功耗管理和散热问题等。 ...

    Inside Microsoft Windows Communication Foundation

    Part I - Introduction to WCF Chapter 1 - The Moon is Blue Chapter 2 - Service Orientation Chapter 3 - Message Exchange Patterns, Topologies, and Choreographies Chapter 4 - WCF 101 Part II -...

    Inside C#--C#编程从入门到精通 pdg

    8. **异步编程**:C#的async/await关键字使得编写异步代码变得简单,这对于处理I/O密集型任务和改善用户体验非常有用。 9. **.NET框架**:C#是.NET框架的一部分,利用.NET库,开发者可以访问大量预先编写的类和功能...

    Ruby中的循环语句的用法教程

    Ruby中的循环用于执行相同的代码块指定的次数。本章将详细介绍Ruby支持的循环语句。 Ruby while 语句: ... puts(Inside the loop i = #$i ) $i +=1 end 这将产生以下结果: Inside the loop i = 0 Inside the lo

    Inside C# Second Edition 书本源码

    6. **异步编程**:掌握C#的async和await关键字,用于编写高性能的非阻塞I/O操作。 通过阅读和实践这些源码,读者不仅可以熟悉C#的基本语法,还能了解到如何在实际项目中应用这些知识,提升编程技能和解决问题的能力...

    大作业任务说明

    i++ ; for j current floor;j&gt; 1&amp;&amp;outside up[j] 0&amp;&amp;outside down[j] 0&amp;&amp;inside[j] 0;j ; if i 10&amp;&amp;j 0 { 上下都找到了 则进行比较 floor i current floor &gt; current ...

    Microsoft - Inside Windows 2000 Third Edition

    4. **性能优化**:书中可能会探讨如何通过内存管理、进程调度、I/O子系统优化来提升Windows 2000的性能。例如,虚拟内存机制、页面文件管理和磁盘缓存的原理。 5. **系统管理**:Windows 2000提供了一系列工具和...

    Inside.Microsoft.NET.IL.Assembler

    To tell the truth, I don't think I had much choice in this matter. Let me explain. With Microsoft .NET technology taking the world by storm, with more and more information professionals getting ...

    06_MP.Windows.Server.2012.R2.Inside.Out.V1.Configuration.Storage.and.Essentials

    Welcome to Windows Server 2012 R2 Inside Out: Configuration, Storage, & Essentials. As the author of many popular technology books, I’ve been writing professionally about Windows and Windows Server ...

    Inside C#, Second Edition

    File I/O with streams &#8226; Error handling with exceptions &#8226; Operator overloading and user-defined conversions &#8226; Delegates and event handlers&#8226; Documentation with XML ADVANCED C#&#...

    linux inside

    - **固定映射(Fixmaps)和I/O重映射**:固定映射提供了一种将物理地址直接映射到内核地址空间的方法,而I/O重映射则用于处理I/O端口的访问。 - **SMP(Symmetric Multi-Processing)**:多处理器环境下,内存管理变得...

    Inside BeansDB

    在数据存储方面,BeansDB采用了Tokyo Cabinet作为底层存储引擎,这是一种轻量级的Key-Value数据库,具有简洁的API和高效的Hash索引机制,能够显著减少I/O操作。同时,BeansDB还支持数据压缩和多文件存储,以提高数据...

    Inside the Microsoft Build Engine: Using MSBuild and Team Foundation Build

    TFB不仅仅是一个简单的构建工具,它还包含了持续集成(CI)、持续部署(CD)等特性,能够帮助团队实现高效协作。 - **功能**: - **自动化构建**:TFB可以自动执行代码构建、测试、打包等操作。 - **持续集成**:...

    图形学考试试题-计算机专业

    - 当\( d )时,下一个像素选在当前像素的左下方,即\( y_{i+1} = y_{i} + 1, x_{i+1} = x_{i} - 1 \)。 - 当\( d = 0 \)时,约定取\( y_{i+1} = y_{i} + 1, x_{i+1} = x_{i} - 1 \)。 **3. 圆弧段扫描转换算法** ...

Global site tag (gtag.js) - Google Analytics