透明思考@CSDN

思考着的程序员,程序员的思考

用户操作
[即时聊天] [发私信] [加为好友]
透明@CSDNID:gigix
947232次访问,排名32好友0人,关注者15
gigix的文章
原创 361 篇
翻译 1 篇
转载 3 篇
评论 1816 篇
最近评论
sap99:www.sap99.com/,SAP99资料多多

SAP免费资料下载
http://www.sap99.com

有很多的学习资料,推荐一下,
shendl:public static AuthorizationService getInstance()

{

if(null == instance){

instance = new AuthorizationService();

}

return instanc……
lishali12345:你真的需要一直那些所谓的大师来摆弄吗?
我只是一个简单的读者而已,你总是拿一些所谓的名人大家的话来盖人,一个目的无非是想增加你自己说话的分量,其实你自己的话就压根没什么分量,基于对自己的不自信才会导致你在所有的文章中,开头以及结尾经常借大家之口来表达你要意淫的某些观点。
实在不忍心那些大家,经常就从你口之中说出来啊!
carry1002:你好,我是猎头公司carry,我们服务的对象主要是世界500强企业,现在有thougthtworks公司的职位机会,TW是敏捷方法领域的领头羊,有兴趣的朋友请和我联系,我的msn:carry.1@hotmail.com
zdonking:很好,感谢gigix前辈的经验分享。
文章分类
收藏
    相册
    我的图片
    测试
    Arrays.asList("Rod", "Jane", "Freddy");(RSS)
    存档
    软件项目交易
    订阅我的博客
    XML聚合  FeedSky
    订阅到鲜果
    订阅到Google
    订阅到抓虾
    订阅到BlogLines
    订阅到Yahoo
    订阅到GouGou
    订阅到飞鸽
    订阅到Rojo
    订阅到newsgator
    订阅到netvibes

    原创 i++和++i的效率差别收藏

    新一篇: Linux CVS Server + jCVS-5.3.2 Client分布式开发(三) | 旧一篇: 探寻软件的永恒之道

    一个无数人讨论过的问题,今天终于看到一个人讲得全面而清楚。下面这个帖子是shornmao (死猫)发的,我只是帮他贴过来而已,希望死猫不会生我的气。
    -----------------------
    首先声明,简单的比较前缀自增运算符和后缀自增运算符的效率是片面的,因为存在很多因素影响这个问题的答案。
    
    
    首先考虑内建数据类型的情况:
    
    如果自增运算表达式的结果没有被使用,而仅仅简单的用于增加一员操作数,答案是明确的,前缀法和后缀法没有任何区别,编译器的处理都应该是相同的,很难想象得出有什么编译器实现可以别出心裁在二者之间制造任何差异。
    测试C++源代码如下:
    //test1.cpp
    void 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,,7
    L3:
    L2:
            leave
            ret
    很显然,不管是i++还是++i都仅仅是一条incl指令而已。
    
    如果表达式的结果被使用,那么情况要稍微复杂一些。
    测试C++源代码如下:
    //test2.cpp
    void 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,,7
    L3:
    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.cpp
    int 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.cpp
    int 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.cpp
    int 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上。
    

    发表于 @ 2002年06月27日 09:56:00|评论(loading...)|编辑

    新一篇: Linux CVS Server + jCVS-5.3.2 Client分布式开发(三) | 旧一篇: 探寻软件的永恒之道

    评论:没有评论。

    发表评论  


    当前用户设置只有注册用户才能发表评论。如果你没有登录,请点击登录
    Csdn Blog version 3.1a
    Copyright © 透明@CSDN