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 i++的更多相关文章
- vsftpd:500 OOPS: vsftpd: refusing to run with writable root inside chroot ()错误的解决方法
---恢复内容开始--- 最近在安装了vsftpd后 添加了虚拟账户后 新建用户 为新用户创立独立的工作目录 因为虚拟用户在工作目录需要上传文件 所以必须拥有此目录的W权限,但每当给此目录加上W权限后 ...
- 解决vsftpd的refusing to run with writable root inside chroot错误
参考 http://www.cnblogs.com/CSGrandeur/p/3754126.html 在Ubuntu下用 vsftpd 配置FTP服务器,配置 “ sudo chmod a-w /h ...
- 《Inside UE4》目录
<Inside UE4>目录 InsideUE4 UE4无疑是非常优秀的世界上最顶尖的引擎之一,性能和效果都非常出众,编辑器工作流也非常的出色,更难得宝贵的是完全的开源让我们有机会去从中吸 ...
- 在子线程中new Handler报错--Can't create handler inside thread that has not called Looper.prepare()
在子线程中new一个Handler为什么会报以下错误? java.lang.RuntimeException: Can't create handler inside thread that has ...
- 《Inside UE4》-0-开篇
<Inside UE4>-0-开篇 InsideUE4 前言 VR行业发展是越来越火热了,硬件设备上有HTC VIVE,Oculus rift,PS VR,各种魔镜:平台上有Steam ...
- 500 OOPS: vsftpd: refusing to run with writable root inside chroot()
Ubuntu 12.04 64bit系统下安装的vsftpd,在登陆时提示500 OOPS: vsftpd: refusing to run with writable root inside chr ...
- 《Inside UE4》-1-基础概念
<Inside UE4>-1-基础概念 InsideUE4 创建测试项目 接上文的准备工作,双击生成的UE4Editor.exe,选择创建测试C++空项目Hello(以后的源码分析 ...
- 《Inside UE4》-2-GamePlay架构(一)Actor和Component
<Inside UE4>-2-GamePlay架构(一)Actor和Component <Inside UE4>-2-GamePlay架构(一)Actor和Compon ...
- VS代码段扩展Snippet Designer is a Visual Studio plug in which allows you to create and search for snippets inside the IDE
Snippet Designer is a Visual Studio plug in which allows you to create and search for snippets insid ...
- Android 线程更新UI报错 : Can't create handler inside thread that has not called Looper.prepare()
MainActivity中有一个按钮,绑定了save方法 public void save(View view) { String title = titleText.getText().toStri ...
随机推荐
- Oracle PGA
PGA(Process Global Area),是server process一段私有内存区,它包含有全局变量,数据结构和一些控制信息.在Oracle8i 中,PGA调整非常复杂,要调整SORT_A ...
- 【BLE】CC2541之加入自己定义任务
本篇博文最后改动时间:2017年01月06日,11:06. 一.简单介绍 本文介绍怎样在SimpleBLEPeripheralproject中.加入一个香瓜任务. (香瓜任务与project原有任务相 ...
- asp.net 定时执行任务代码 定时采集数据
using System; using System.Data; using System.Configuration; using System.Collections; using System. ...
- 第二周 Word版面设计
第二周 Word版面设计 教学时间 2013-3-5 教学课时 2 教案序号 1 教学目标 1.能正确设置纸张.版心.视图.分栏.页眉页脚2.掌握节的概念并能正确使用 教学过程: 新课 要使一篇文档美 ...
- percona-Toolkit
1:下载最新安装包 wget https://www.percona.com/downloads/percona-toolkit/2.1.1/percona-toolkit-2.1.1.tar.gz ...
- 〖Linux〗将Jar文件打包成Linux可执行文件
因为不想敲写 java -jar jarfile,而是想直接敲写 ./jarfile 就能够跑起程序 #!/bin/bash - #================================= ...
- Android插件化开发之解决OpenAtlas组件在宿主的注冊问题
OpenAtlas有一个问题,就是四大组件必须在Manifest文件里进行注冊,那么就必定带来一个问题,插件中的组件都要反复在宿主中注冊.像Service,ContentProvider等组件眼下没有 ...
- Ubuntu中的Gif动画录制工具
为了在随笔中插入gif动态图Windows系统上可以使用ScreenToGif这个非常好用的小软件,在Ubuntu系统中选择也很多(可以参考最下面的链接),下面介绍两款ubuntu系统中的录屏软件: ...
- MYSQL数据库注释
//修改注释 alter table user comment = '我要修改注释'; //新建表设定表注释及解释说明. create table AuthUser( ID ) primary key ...
- linux 上配置tomcat、mysql 开机启动
1.tomcat 开机启动 方法一.修改系统文件 (已经尝试,可以) 1.修改/etc/rc.d/rc.local vi /etc/rc.d/rc.local 2.添加下面两行脚本,记住是两行,仅仅第 ...