confide

导航

i++,++i, i=i+1 vs编译器上的深入研究

首先三者都是等价的操作,三者的效率也都是等价的,这是vs2010的反汇编,三者都被转成相同的汇编代码:

 
01041375  mov         eax,dword ptr [i]  
01041378  add         eax,1  
0104137B  mov         dword ptr [i],eax  
 
三者在表达式中需要注意的:
 
    i++ = 1;  (错误,语法提示i++不能作为左值)
    ++i = 1;  (正确)
 
如下程序:
        int i = 0;
++i = 0;
结果i为0,说明++i=0;表达式的解释为:
        ++i=0;  ------>          i=i+1;
                                          i=0;
对于输出的奇怪现象:
 
        int i = 0;
 
cout<<i++<<" "<<i++<<endl;
cout<<i<<endl;
//1 0
//2
i=0;
cout<<i++<<" "<<++i<<endl;
cout<<i<<endl;
//1 2
//2
i=0;
cout<<++i<<" "<<++i<<endl;
cout<<i<<endl;
//2 2
//2
i=0;
cout<<++i<<" "<<i++<<endl;
cout<<i<<endl;
//2 0
//2
 
对其汇编代码的查看得知,首先简单的说明下面两个输出的汇编:
            cout<<i++<<endl;
            ------->

00B94295  mov         eax,dword ptr [i]  
00B94298  mov         dword ptr [ebp-0D0h],eax  
00B9429E  mov         ecx,dword ptr [i]  
00B942A1  add         ecx,1  
00B942A4  mov         dword ptr [i],ecx  
00B942A7  mov         esi,esp  
00B942A9  mov         edx,dword ptr [__imp_std::endl (0B9A314h)]  
00B942AF  push        edx  
00B942B0  mov         edi,esp  
00B942B2  mov         eax,dword ptr [ebp-0D0h]  
00B942B8  push        eax  
00B942B9  mov         ecx,dword ptr [__imp_std::cout (0B9A318h)]  
00B942BF  call        dword ptr [__imp_std::basic_ostream<char,std::char_traits<char> >::operator<< (0B9A31Ch)]  
00B942C5  cmp         edi,esp  
00B942C7  call        @ILT+395(__RTC_CheckEsp) (0B91190h)  
00B942CC  mov         ecx,eax  
00B942CE  call        dword ptr [__imp_std::basic_ostream<char,std::char_traits<char> >::operator<< (0B9A300h)]  
00B942D4  cmp         esi,esp  
00B942D6  call        @ILT+395(__RTC_CheckEsp) (0B91190h)  

如红色标注,发现编译器在i++的基本汇编代码中插入了一句,即先将取出来的i的值存入临时变量中,然后在打印的时候从临时变量中取出压进输出栈。

            cout<<++i<<endl;
            --------->
00CA42DB  mov         eax,dword ptr [i]  
00CA42DE  add         eax,1  
00CA42E1  mov         dword ptr [i],eax  
00CA42E4  mov         esi,esp  
00CA42E6  mov         ecx,dword ptr [__imp_std::endl (0CAA314h)]  
00CA42EC  push        ecx  
00CA42ED  mov         edi,esp  
00CA42EF  mov         edx,dword ptr [i]  
00CA42F2  push        edx  
00CA42F3  mov         ecx,dword ptr [__imp_std::cout (0CAA318h)]  
00CA42F9  call        dword ptr [__imp_std::basic_ostream<char,std::char_traits<char> >::operator<< (0CAA31Ch)]  
00CA42FF  cmp         edi,esp  
00CA4301  call        @ILT+395(__RTC_CheckEsp) (0CA1190h)  
00CA4306  mov         ecx,eax  
00CA4308  call        dword ptr [__imp_std::basic_ostream<char,std::char_traits<char> >::operator<< (0CAA300h)]  
00CA430E  cmp         esi,esp  
00CA4310  call        @ILT+395(__RTC_CheckEsp) (0CA1190h)  
发现,编译器并没有在++i的基本汇编中插入任何东西,而是在要打印的时候直接从变量i中取出压进输出栈。

知道这两点不同就可以很好的理解上述的奇怪现象。
还是需要记住两点的是:
1、在整个输出语句中,都是先对所有的表达式求值后(保存相应的输出信息)再执行输出操作。
2、cout<<i  这种样式的输出只是调用operator <<成员函数的简单形式,
下面一个一个汇编代码来解析:
        cout<<i++<<" "<<i++<<endl;
--------------->
00CA4315  mov         eax,dword ptr [i]  
00CA4318  mov         dword ptr [ebp-0D0h],eax  
00CA431E  mov         ecx,dword ptr [i]  
00CA4321  add         ecx,1  
00CA4324  mov         dword ptr [i],ecx  
00CA4327  mov         edx,dword ptr [i]  
00CA432A  mov         dword ptr [ebp-0D4h],edx  
00CA4330  mov         eax,dword ptr [i]  
00CA4333  add         eax,1  
00CA4336  mov         dword ptr [i],eax  
00CA4339  mov         esi,esp  
00CA433B  mov         ecx,dword ptr [__imp_std::endl (0CAA314h)]  
00CA4341  push        ecx  
00CA4342  mov         edi,esp  
00CA4344  mov         edx,dword ptr [ebp-0D0h]  
00CA434A  push        edx  
00CA434B  push        offset string " " (0CA7830h)  
00CA4350  mov         ebx,esp  
00CA4352  mov         eax,dword ptr [ebp-0D4h]  
00CA4358  push        eax  
00CA4359  mov         ecx,dword ptr [__imp_std::cout (0CAA318h)]  
00CA435F  call        dword ptr [__imp_std::basic_ostream<char,std::char_traits<char> >::operator<< (0CAA31Ch)]  
00CA4365  cmp         ebx,esp  
00CA4367  call        @ILT+395(__RTC_CheckEsp) (0CA1190h)  
00CA436C  push        eax  
00CA436D  call        std::operator<<<std::char_traits<char> > (0CA1145h)  
00CA4372  add         esp,8  
00CA4375  mov         ecx,eax  
00CA4377  call        dword ptr [__imp_std::basic_ostream<char,std::char_traits<char> >::operator<< (0CAA31Ch)]  
00CA437D  cmp         edi,esp  
00CA437F  call        @ILT+395(__RTC_CheckEsp) (0CA1190h)  
00CA4384  mov         ecx,eax  
00CA4386  call        dword ptr [__imp_std::basic_ostream<char,std::char_traits<char> >::operator<< (0CAA300h)]  
00CA438C  cmp         esi,esp  
00CA438E  call        @ILT+395(__RTC_CheckEsp) (0CA1190h)  
红色部分即为核心,首先计算第二个i++表达式,再计算第一个,保存信息到临时变量中,即dword ptr [ebp-0D0h] 和dword ptr [ebp-0D4h]中,完成后,先将第二个其值从临时变量中取出压入输出栈,再操作第一个。
 
 
        cout<<i++<<" "<<++i<<endl;
----------------->
00CA43CA  mov         eax,dword ptr [i]  
00CA43CD  add         eax,1  
00CA43D0  mov         dword ptr [i],eax  
00CA43D3  mov         ecx,dword ptr [i]  
00CA43D6  mov         dword ptr [ebp-0D0h],ecx  
00CA43DC  mov         edx,dword ptr [i]  
00CA43DF  add         edx,1  
00CA43E2  mov         dword ptr [i],edx  
00CA43E5  mov         esi,esp  
00CA43E7  mov         eax,dword ptr [__imp_std::endl (0CAA314h)]  
00CA43EC  push        eax  
00CA43ED  mov         edi,esp  
00CA43EF  mov         ecx,dword ptr [i]  
00CA43F2  push        ecx  
00CA43F3  push        offset string " " (0CA7830h)  
00CA43F8  mov         ebx,esp  
00CA43FA  mov         edx,dword ptr [ebp-0D0h]  
00CA4400  push        edx  
00CA4401  mov         ecx,dword ptr [__imp_std::cout (0CAA318h)]  
00CA4407  call        dword ptr [__imp_std::basic_ostream<char,std::char_traits<char> >::operator<< (0CAA31Ch)]  
00CA440D  cmp         ebx,esp  
00CA440F  call        @ILT+395(__RTC_CheckEsp) (0CA1190h)  
00CA4414  push        eax  
00CA4415  call        std::operator<<<std::char_traits<char> > (0CA1145h)  
00CA441A  add         esp,8  
00CA441D  mov         ecx,eax  
00CA441F  call        dword ptr [__imp_std::basic_ostream<char,std::char_traits<char> >::operator<< (0CAA31Ch)]  
00CA4425  cmp         edi,esp  
00CA4427  call        @ILT+395(__RTC_CheckEsp) (0CA1190h)  
00CA442C  mov         ecx,eax  
00CA442E  call        dword ptr [__imp_std::basic_ostream<char,std::char_traits<char> >::operator<< (0CAA300h)]  
00CA4434  cmp         esi,esp  
00CA4436  call        @ILT+395(__RTC_CheckEsp) (0CA1190h)  
这个就是我们不明白其操作原理的最奇怪的例子了,和之前解释的一样,将++i的输出信息压栈的时候是直接从变量i中取出来的值,所以是2(表达式全都计算完的结果),而i++则是保存的临时值(计算i++表达式时保存的临时值)。
 
 
知道了这些,下面的两个例子就很好解释了。
        cout<<++i<<" "<<++i<<endl;
//2 2
i=0;
cout<<++i<<" "<<i++<<endl;
//2 0
 
 
但是为什么会这样?还是不从得知。

posted on 2012-03-08 20:10  confide  阅读(1047)  评论(0编辑  收藏  举报