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++这种无聊的语句呢?
不过却可以了解一下编译器(解释器)的工作方式。
——
(版权所有,如需转载,请注明作者和出处)
分享到:
相关推荐
描述中的"it's been over a decade since I started writing applications for Microsoft Windows"暗示了作者有丰富的Windows平台开发经验,因此这个压缩包中的内容可能是作者多年实践和理解的结晶,涵盖了从基础的...
标题“Inside the BMW Cars entertainement Serial Bu”可能是指I-BUS系统的内部结构和工作原理。I-BUS(Inter Equipment Bus)是一种用于汽车内部通信的总线系统,用于连接汽车内的多个电子控制单元(ECUs)以及...
标题 "i2c-rk3x.rar_inside" 暗示了我们正在处理与RK3x系列处理器相关的I2C(Inter-Integrated Circuit)通信协议的实现,其中".rar_inside"可能表示这是一个压缩包中的文件,包含了对RK3x芯片I2C功能的详细配置或...
根据提供的文件信息,我们可以推断出这是一本关于Smalltalk编程语言的书籍,书名为《Inside Smalltalk Volume I》。本书由W. LaLonde与J.R. Pugh合著,作者均来自卡尔顿大学计算机科学学院。此书旨在介绍Smalltalk的...
《Inside the Native API》是关于Windows NT操作系统内核函数的权威文档,对于深入理解Windows系统底层运作机制,尤其是驱动程序和内核级开发而言,它是一份不可或缺的参考资料。这份文档详细介绍了原生API(Native ...
6. **总线与I/O子系统**:书中还探讨了计算机内部的数据传输机制,包括不同类型的总线和I/O接口。 7. **高级主题**:此外,《Inside the Machine》还涉及了一些更高级的话题,如虚拟化技术、功耗管理和散热问题等。 ...
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 -...
8. **异步编程**:C#的async/await关键字使得编写异步代码变得简单,这对于处理I/O密集型任务和改善用户体验非常有用。 9. **.NET框架**:C#是.NET框架的一部分,利用.NET库,开发者可以访问大量预先编写的类和功能...
Ruby中的循环用于执行相同的代码块指定的次数。本章将详细介绍Ruby支持的循环语句。 Ruby while 语句: ... puts(Inside the loop i = #$i ) $i +=1 end 这将产生以下结果: Inside the loop i = 0 Inside the lo
6. **异步编程**:掌握C#的async和await关键字,用于编写高性能的非阻塞I/O操作。 通过阅读和实践这些源码,读者不仅可以熟悉C#的基本语法,还能了解到如何在实际项目中应用这些知识,提升编程技能和解决问题的能力...
i++ ; for j current floor;j> 1&&outside up[j] 0&&outside down[j] 0&&inside[j] 0;j ; if i 10&&j 0 { 上下都找到了 则进行比较 floor i current floor > current ...
4. **性能优化**:书中可能会探讨如何通过内存管理、进程调度、I/O子系统优化来提升Windows 2000的性能。例如,虚拟内存机制、页面文件管理和磁盘缓存的原理。 5. **系统管理**:Windows 2000提供了一系列工具和...
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 ...
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 ...
File I/O with streams • Error handling with exceptions • Operator overloading and user-defined conversions • Delegates and event handlers• Documentation with XML ADVANCED C#&#...
- **固定映射(Fixmaps)和I/O重映射**:固定映射提供了一种将物理地址直接映射到内核地址空间的方法,而I/O重映射则用于处理I/O端口的访问。 - **SMP(Symmetric Multi-Processing)**:多处理器环境下,内存管理变得...
在数据存储方面,BeansDB采用了Tokyo Cabinet作为底层存储引擎,这是一种轻量级的Key-Value数据库,具有简洁的API和高效的Hash索引机制,能够显著减少I/O操作。同时,BeansDB还支持数据压缩和多文件存储,以提高数据...
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. 圆弧段扫描转换算法** ...