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

i++和++i的效率差别

阅读更多
一个无数人讨论过的问题,今天终于看到一个人讲得全面而清楚。下面这个帖子是shornmao (死猫)发的,我只是帮他贴过来而已,希望死猫不会生我的气。
-----------------------
首先声明,简单的比较前缀自增运算符和后缀自增运算符的效率是片面的,因为存在很多因素影响这个问题的答案。首先考虑内建数据类型的情况:如果自增运算表达式的结果没有被使用,而仅仅简单的用于增加一员操作数,答案是明确的,前缀法和后缀法没有任何区别,编译器的处理都应该是相同的,很难想象得出有什么编译器实现可以别出心裁在二者之间制造任何差异。测试C++源代码如下://test1.cppvoid test(){int i=0;i++;++i;}Gnu C/C++ 2编译的汇编中间代码如下:        .file   "test1.cpp"gcc2_compiled.:___gnu_compiled_cplusplus:.text        .align 4.globl _test__Fv        .def    _test__Fv;      .scl    2;      .type   32;     .endef_test__Fv:        pushl %ebp        movl %esp,%ebp        subl $24,%esp        movl $0,-4(%ebp)	;i=0        incl -4(%ebp)		;i++        incl -4(%ebp)		;++i        jmp L3        jmp L2        .p2align 4,,7L3:L2:        leave        ret很显然,不管是i++还是++i都仅仅是一条incl指令而已。如果表达式的结果被使用,那么情况要稍微复杂一些。测试C++源代码如下://test2.cppvoid test(){int i=0,a,b;a=i++;b=++i;}Gnu C/C++ 2编译的汇编中间代码如下:	.file	"test2.cpp"gcc2_compiled.:___gnu_compiled_cplusplus:.text	.align 4.globl _test__Fv	.def	_test__Fv;	.scl	2;	.type	32;	.endef_test__Fv:	pushl %ebp	movl %esp,%ebp	subl $24,%esp	movl $0,-4(%ebp)		;i=0	movl -4(%ebp),%eax		;i --> ax	movl %eax,-8(%ebp)		;ax --> a(a=i)	incl -4(%ebp)			;i++	incl -4(%ebp)			;++i	movl -4(%ebp),%eax		;i --> ax	movl %eax,-12(%ebp)		;ax --> b(b=i)	jmp L3	jmp L2	.p2align 4,,7L3:L2:	leave	ret有差别吗?显然也没有,同样是一条incl指令,再加上两条movl指令借用eax寄存器复制调用栈内容。让我们再加上编译器优化,重新编译后的汇编代码如下:	.file	"test2.cpp"gcc2_compiled.:___gnu_compiled_cplusplus:.text	.align 4.globl _test__Fv	.def	_test__Fv;	.scl	2;	.type	32;	.endef_test__Fv:	pushl %ebp	movl %esp,%ebp	leave	ret好了,优化的过火了,由于i,a,b三个变量没有被使用,所以干脆全都被优化了,结果成了一个什么都不做的空函数体。那么,让我们再加上一点代码使用a和b的结果吧,这样i的结果也不能够忽略了,C++源代码如下://test3.cppint test(){int i=0,a,b;a=i++;b=++i;return a+b;}此时汇编代码如下:	.file	"test3.cpp"gcc2_compiled.:___gnu_compiled_cplusplus:.text	.align 4.globl _test__Fv	.def	_test__Fv;	.scl	2;	.type	32;	.endef_test__Fv:	pushl %ebp	movl %esp,%ebp	movl $2,%eax	leave	ret你还是没有想到吧,答案仅仅是编译器计算了返回值,常量展开(constant-unwinding)启动,变成了直接返回常量结果。怎么办?我们把i变成参数,避免这种预期以外的结果,C++源代码如下://test4.cppint test1(int i){int a=i++;return a;}int test2(int i){int a=++i;return a;}好了,很辛苦,终于得到了不一样的汇编代码:	.file	"test4.cpp"gcc2_compiled.:___gnu_compiled_cplusplus:.text	.align 4.globl _test1__Fi	.def	_test1__Fi;	.scl	2;	.type	32;	.endef_test1__Fi:	pushl %ebp	movl %esp,%ebp	movl 8(%ebp),%eax	leave	ret	.align 4.globl _test2__Fi	.def	_test2__Fi;	.scl	2;	.type	32;	.endef_test2__Fi:	pushl %ebp	movl %esp,%ebp	movl 8(%ebp),%eax	incl %eax	leave	ret和你接触到的教条正相反吧,++i反而增加了一条汇编指令incl,而i++却没有,这就是编译器优化的魅力。因为不管i有没有增加,都不影响a的值,而函数仅仅返回i的值,所以i的自增运算就根本不必进行了。所以,为了更客观一些,我们将i参数改为按照引用传递,C++源代码如下;//test5.cppint test1(int &i){int a=i++;return a;}int test2(int &i){int a=++i;return a;}这一次的结果加入了指针的运算,稍微复杂一些:	.file	"test5.cpp"gcc2_compiled.:___gnu_compiled_cplusplus:.text	.align 4.globl _test1__FRi	.def	_test1__FRi;	.scl	2;	.type	32;	.endef_test1__FRi:	pushl %ebp	movl %esp,%ebp	movl 8(%ebp),%eax	movl (%eax),%edx	incl (%eax)	movl %edx,%eax	leave	ret	.align 4.globl _test2__FRi	.def	_test2__FRi;	.scl	2;	.type	32;	.endef_test2__FRi:	pushl %ebp	movl %esp,%ebp	movl 8(%ebp),%eax	movl (%eax),%edx	leal 1(%edx),%ecx	movl %ecx,(%eax)	movl %ecx,%eax	leave	ret惊讶吗?还是a=i++的代码更高效一些,不知道这会让你有什么想法。反正,我得出的结论,对于内建数据类型来说,i++和++i孰优孰劣,是编译器实现相关的,实在不必太可以关心这个问题。最后让我们再回到起点,对于自定义数据类型(主要是指类)说,不需要再做很多汇编代码的分析了,我很清楚的知道为什么会有人循循善诱。因为前缀式可以返回对象的引用,而后缀式必须返回对象的值,所以导致了在大对象的时候产生了较大的复制开销,引起效率降低,因此会有劝告尽量使用前缀式,尽可能避免后缀式,除非从行为上真的需要后缀式。这也就是More Effective C++/Term 7中的原文提到的,处理使用者自定义类型(注意不是指内建类型)的时候,应该尽可能的使用前缀式地增/递减,因为他天生体质较佳。同时,为了保证前缀和后缀对递增/递减的语义的实现保持一致,设计上的一般原则是后缀式的实现以前缀式为基础,这样,后缀式往往多了一次函数调用,这也许也是一个需要考虑的效率因素,不过相比之下,就有点微乎其微了。重申一点关于这个问题的进一步叙述,可以在Scott Mayer的<<More Effective C++>>一书的条款7中获得,大约在原书的P31-34上。
分享到:
评论

相关推荐

    谈谈Java中的i++

    这是和JVM的内存分配有关,JVM在处理这段带代码时,会先把i++的结果赋值给一个临时变量temp,然后再将这个临时变量的值赋值给i。即如下: int i = 0; int temp; // i = i++; int a = temp = i++;// 临时变量...

    浅析PHP中的i++与++i的区别及效率

    当然如果编译器会将这些差别都优化掉,那么效率就都差不多了。 再给大家详细说下++i 与 i++ 的区别 1、++i 的用法(以 a=++i ,i=2 为例) 先将 i 值加 1 (也就是 i=i+1 ),然后赋给变量 a (也就

    C++面试题:++i和i++哪个效率高?

     2、我们自定的数据类型,++i效率高于i++,通过运算符重载来给大家说明这一点。 Operator Operator::operator++() { ++value; //内部成员变量 return *this; } Operator Operator::operator++(int) { ...

    浅析PHP中的i++与++i的区分及效率_.docx

    浅析PHP中的i++与++i的区分及效率_.docx

    i++循环与i-–循环的执行效率(递增与递减效率)

    i++循环与i-–循环的执行效率(递增与递减效率),需要的朋友可以参考下。

    探讨++i与i++哪个效率更高

    在自定义数据类型的情况下,++i效率更高! 分析: (自定义数据类型的情况下) ++i返回对象的引用; i++总是要创建一个临时对象,在退出函数时还要销毁它,而且返回临时对象的值时还会调用其拷贝构造函数。 (重载这两个...

    数据结构(Java)复习题

    分析算法的效率以求改进 D. 分析算法的易懂性和文档性 ② A. 空间复杂性和时间复杂性 B. 正确性和简明性 C. 可读性和文档性 D. 数据复杂性和程序复杂性 5. 计算机算法指的是① ,它必具备输入、输出和② 等五个特性...

    springboot i+redis+mybatis+shiro实现的权限管理系统+极速代码一键生成器

    这是一个基于微型项目开发的架构。权限管理系统,采用shiro实现权限。里面有代码生成器,可基于数据表快速生成crud接口+页面。可学习与商用。非常实用的一套架构。导入idea即配置mysql+即可使用

    PC-lint9.0i+补丁+教程

    PC-Lint 是GIMPEL SOFTWARE公司开发的C/C++软件代码静态分析工具,它的全称是PC-Lint/FlexeLint for C/C++,PC-Lint 能够在Windows、MS-DOS和OS/2平台上使用,以二进制可执行文件的形式发布,而FlexeLint 运行于其它...

    给定一个整数n,求出所有连续的且和为n正整数

    为了提高计算的效率,程序所采用的算法如下:(1) 从1开始计算连续的整数和sum,直到sum不小于n为止;(2) 在第i步,如果sum=i+(i+1)+…+j比n大,则去掉连加的最左端的数i,如果sum比n小,在连加的右端加上一个数(j+1)...

    12864显示的万年历

    //坐标轴的移动和反转 if(x) { LCDSel(1); SetX(y/8); //set the page address SetY(x); //set the Y adress dat=ReadDatFromLCD12864(); if(flag) dat=dat|(Tab[y%8]); else dat=dat&(~(Tab...

    android中几种for循环的效率

    在网上看了一些for循环的效率问题,发现基本上都是一些理论的东西,且大多都是Copy来的,前后文自相矛盾。自己做了一个例子,来看看他们的效率 1.最慢 private long getTime1() { long time = 0; int count = 0;...

    数据结构第一章答案

    数据结构及应用算法教程(修订版)(严蔚敏 陈博文)第一章习题答案

    逆变三电平I型和T型电路的比较分析.pdf

    逆变三电平I型和T型电路的比较分析pdf,随着太阳能、UPS 技术的不断发展和市场的不断扩大,对逆变器效率的要求也越来越被制造商所重视,因此三电平的拓扑结构便应运而生。众所周知,与传统两电平结构相比,三电平结构...

    冒泡排序优化算法

    冒泡排序的优化算法,提高了效率int main(){ int a, m; cin&gt;&gt;m; int *p; p = new int[m]; for(int i=0;i;i++) cin&gt;&gt;p[i]; int temp; for(int i=0;i;i++){ a = true; for(int j=m-2;j&gt;=i;j--) if(p[j+1][j...

    香农编码实验报告(1).doc

    一、实验目的 实验仙农编码算法 二、实验步骤 1、输入信源个数n 2、输入n个信源的概率 3、由大到小重新排列信源 4、实现信源概率的叠加 5、计算码长l 6、编码 7、计算平均码长pl、信源熵Hx以及编码效率q 三、源程序...

    php基于CI+layui开发的个人博客系统.zip

    近年来,PHP持续进行性能改进与新特性的引入,如PHP 7系列版本在速度上有了显著提升,新增了类型声明、标量类型提示、null合并运算符等语法特性,进一步提升了开发效率和代码质量。 总的来说,PHP作为一种成熟的Web...

    ESP32微控制器 上 I2S 支持的用法和示例_设计_文档_相关文件_下载

    或者,使用预构建的固件二进制文件之一下载和编程您的 ESP32 板。 使用给定的参数构造并返回一个新的 I2S 对象: id指定 I2S 外设实例 位时钟输出的bck引脚对象 用于单词选择输出的ws pin 对象 用于串行数据输入的...

Global site tag (gtag.js) - Google Analytics