i和i的效率差别_第1页
i和i的效率差别_第2页
i和i的效率差别_第3页
i和i的效率差别_第4页
i和i的效率差别_第5页
已阅读5页,还剩3页未读 继续免费阅读

下载本文档

版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领

文档简介

i++和++i的效率差别一个无数人讨论过的问题,今天终于看到一个人讲得全面而清楚。下面这个帖子是shornmao(死猫)发的,我只是帮他贴过来而已,希望死猫不会生我的气。首先声明,简单的比较前缀自增运算符和后缀自增运算符的效率是片面的,因为存在很多因素影响这个问题的答案。首先考虑内建数据类型的情况:如果自增运算表达式的结果没有被使用,而仅仅简单的用于增加一员操作数,答案是明确的,前缀法和后缀法没有任何区别,编译器的处理都应该是相同的,很难想象得出有什么编译器实现可以别出心裁在二者之间制造任何差异。测试C++源代码如下://test1.cppvoidtest(){inti=0;i++;++i;}GnuC/C++2编译的汇编中间代码如下:.file"test1.cpp"gcc2_compiled.:___gnu_compiled_cplusplus:.text.align4.globl_test__Fv.def_test__Fv;.scl2;.type32;.endef_test__Fv:pushl%ebpmovl%esp,%ebpsubl$24,%espmovl$0,-4(%ebp);i=0incl-4(%ebp);i++incl-4(%ebp);++ijmpL3jmpL2.p2align4,,7L3:L2:leaveret很显然,不管是i++还是++i都仅仅是一条incl指令而已。如果表达式的结果被使用,那么情况要稍微复杂一些。测试C++源代码如下://test2.cppvoidtest(){inti=0,a,b;a=i++;b=++i;}GnuC/C++2编译的汇编中间代码如下:.file"test2.cpp"gcc2_compiled.:___gnu_compiled_cplusplus:.text.align4.globl_test__Fv.def_test__Fv;.scl2;.type32;.endef_test__Fv:pushl%ebpmovl%esp,%ebpsubl$24,%espmovl$0,-4(%ebp);i=0movl-4(%ebp),%eax;i-->axmovl%eax,-8(%ebp);ax-->a(a=i)incl-4(%ebp);i++incl-4(%ebp);++imovl-4(%ebp),%eax;i-->axmovl%eax,-12(%ebp);ax-->b(b=i)jmpL3jmpL2.p2align4,,7L3:L2:leaveret有差别吗?显然也没有,同样是一条incl指令,再加上两条movl指令借用eax寄存器复制调用栈内容。让我们再加上编译器优化,重新编译后的汇编代码如下:.file"test2.cpp"gcc2_compiled.:___gnu_compiled_cplusplus:.text.align4.globl_test__Fv.def_test__Fv;.scl2;.type32;.endef_test__Fv:pushl%ebpmovl%esp,%ebpleaveret好了,优化的过火了,由于i,a,b三个变量没有被使用,所以干脆全都被优化了,结果成了一个什么都不做的空函数体。那么,让我们再加上一点代码使用 a和b的结果吧,这样i的结果也不能够忽略了, C++源代码如下://test3.cppinttest(){inti=0,a,b;a=i++;b=++i;returna+b;}此时汇编代码如下:.file"test3.cpp"gcc2_compiled.:___gnu_compiled_cplusplus:.text.align4.globl_test__Fv.def_test__Fv;.scl2;.type32;.endef_test__Fv:pushl%ebpmovl%esp,%ebpmovl$2,%eaxleaveret启动,你还是没有想到吧,答案仅仅是编译器计算了返回值,常量展开(constant-unwinding)变成了直接返回常量结果。启动,怎么办?我们把i变成参数,避免这种预期以外的结果,C++源代码如下://test4.cppinttest1(inti){inta=i++;returna;}inttest2(inti){inta=++i;returna;好了,很辛苦,终于得到了不一样的汇编代码:.file"test4.cpp"gcc2_compiled.:___gnu_compiled_cplusplus:.text.align4.globl_test1__Fi.def_test1__Fi;.scl2;.type32;.endef_test1__Fi:pushl%ebpmovl%esp,%ebpmovl8(%ebp),%eaxleaveret.align4.globl_test2__Fi.def_test2__Fi;.scl2;.type32;.endef_test2__Fi:pushl%ebpmovl%esp,%ebpmovl8(%ebp),%eaxincl%eaxleaveret和你接触到的教条正相反吧,++i反而增加了一条汇编指令incl,而i++却没有,这就是编译器优化的魅力。因为不管i有没有增加,都不影响a的值,而函数仅仅返回i的值,所以i的自增运算就根本不必进行了。所以,为了更客观一些,我们将i参数改为按照引用传递,C++源代码如下;//test5.cppinttest1(int&i){inta=i++;returna;}inttest2(int&i){inta=++i;returna;}这一次的结果加入了指针的运算,稍微复杂一些:.file"test5.cpp"gcc2_compiled.:___gnu_compiled_cplusplus:.text.align4.globl_test1__FRi.def_test1__FRi;.scl2;.type32;.endef_test1__FRi:pushl%ebpmovl%esp,%ebpmovl8(%ebp),%eaxmovl(%eax),%edxincl(%eax)movl%edx,%eaxleaveret.align4.globl_test2__FRi.def_test2__FRi;.scl2;.type32;.endef_test2__FRi:pushl%ebpmovl%esp,%ebpmovl8(%ebp),%eaxmovl(%eax),%edxleal1(%edx),%ecxmovl%ecx,(%eax)movl%ecx,%eaxleaveret惊讶吗?还是a=i++的代码更高效一些,不知道这会让你有什么想法。反正,我得出的结论,对于内建数据类型来说,i++和++i孰优孰劣,是编译器实现相关的,实在不必太可以关心这个问题。最后让我们再回到起点,对于自定义数据类型(主要是指类)说,不需要再做很多汇编代码的分析了,我很清楚的知道为什么会有人循循善诱。因为前缀式可以返回对象的引用,而后缀式必须返回对象的值,所以导致了在大对象的时候产生了较大的复制开销,引起效率降低,因此会有劝告尽量使用前缀式,尽可能避免后缀式,除非从行为上真的需要后缀式。这也就是MoreEffectiveC++/Term7中的原文提到的,处理使用者自定义类型(注意不是指内建类型)的时候,应该尽可能的使用前缀式地增 /递减,因为他天生体质较佳。同时,为了保证前缀和后缀对递增/递减的

温馨提示

  • 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
  • 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
  • 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
  • 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
  • 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
  • 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
  • 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。

评论

0/150

提交评论