x=x++带来的困惑。。。

本文探讨了C++中x=x++表达式的实际运行结果,分析了不同数据类型下(如int与迭代器)编译器的行为差异及背后的优化原理,并结合反汇编结果深入解释了这一现象。

今天微软实习生笔试,有一个题目,本来我认为没有什么值得考虑的,结果出来同学一讨论,出现一边倒的结果,当然我不是那一边的,我就想当郁闷呀。

问题主要是:

int x = 10;
x = x++;
cout<<x<<endl;
我想也没想,这结果当然是10了,可以同学回来都说是11,然后再机器上一测试,结果果然是11,我感觉整个人生观被颠覆了。

我清楚的记得后置形式自增/自减,的计算顺序是:先保存操作数原来的值,然后对操作数进行自增/自减操作,然后返回之前保存的操作数的值即未进行自增/自减操作的值。在C++ Primer第四版 P140也是这么说的:


Fig 1

由C++ Primer说法可知,之所以必须先保存操作数原来的值,是因为在返回自增表达式结果之前,会对操作数进行自增运算。

但现在的结果变成了把自增操作放到了复制操作的后面,下面是反汇编的结果:

    int x = 10;
0041191D  mov         dword ptr [ebp-14h],0Ah 
    x = x++;
00411924  mov         eax,dword ptr [ebp-14h] 
00411927  mov         dword ptr [ebp-14h],eax 
0041192A  mov         ecx,dword ptr [ebp-14h] 
0041192D  add         ecx,1 
00411930  mov         dword ptr [ebp-14h],ecx 

在Fig 1中有这么一句话: 对于int型对象和指针,编译器可优化点这项额外工作,但是对于更多的复杂迭代器 类型,这种额外工作可能会花费更大的代价

所以我做了下面的测试:

    vector<int> y;
    for (int i = 0; i < 10; ++i)
    {
        y.push_back(i);
    }

    vector<int>::iterator itr = y.begin();
    while(itr != y.end())
    {
        itr = itr++;
        cout<<*itr<<endl;
    }
如果说和上面的x一样的话,迭代器解引用会输出y中的所有元素(当然这里代码有点问题,就是itr会指向end,会出错,但不影响测试)。但是结果是死循环,始终输出0,这也就表明:itr = itr++的计算顺序是先保存itr的值,然后对itr进行++操作,然后把事先保存的itr的值赋给itr。

下面是反汇编结果的代码

    itr=itr++;
00411A16  push        0    
00411A18  lea         eax,[ebp-13Ch] 
00411A1E  push        eax  
00411A1F  lea         ecx,[ebp-5Ch] 
00411A22  call        std::_Vector_iterator<int,std::allocator<int> >::operator++ (41100Ah) 
00411A27  mov         dword ptr [ebp-164h],eax 
00411A2D  mov         ecx,dword ptr [ebp-164h] 
00411A33  mov         dword ptr [ebp-168h],ecx 
00411A39  mov         byte ptr [ebp-4],3 
00411A3D  mov         edx,dword ptr [ebp-168h] 
00411A43  push        edx  
00411A44  lea         ecx,[ebp-5Ch] 
00411A47  call        std::_Vector_iterator<int,std::allocator<int> >::operator= (41100Fh) 
00411A4C  mov         byte ptr [ebp-4],1 
00411A50  lea         ecx,[ebp-13Ch] 
00411A56  call        std::_Vector_iterator<int,std::allocator<int> >::~_Vector_iterator<int,std::allocator<int> > (411064h) 
由反汇编的结果可以知道,是先保存itr的值,然后对itr进行++操作,然后把事先保存的itr的值赋给itr。

所以可知对于int型对象和指针,编译器进行了优化,直接把x的值赋给了表达式左边的值,然后对x进行自增,而对于迭代器类型,并没有进行优化;

所以说:x从C标准定义的结果应该是10,而不是11。

Apr 6, 2013 @lab

博客内容修改:

C/C++标准除了对操作符:&&,||, ?:,,(逗号操作符)这四个操作符定义了求值顺序,对于其他的操作符都未定义求值顺序,所以上面所讨论的问题,只是编译器的实现问题,标准从未定义过,具体可参考C++ Primer P149 ,和http://bbs.youkuaiyun.com/topics/370153775,谢谢1楼和2楼的提醒

修改时间:Apr 7, 2013 

``` #include <bits/stdc++.h> #define ll long long using namespace std; const ll N=2e5; ll n; ll a[N+5]; ll pre[N+5],pos[N+5]; ll tr[4*N+5],lz[4*N+5]; ll ans; void update(ll p){ tr[p]=tr[2*p]+tr[2*p+1]; return; } void pushdown(ll p,ll l,ll r){ if(!lz[p]) return; ll mid=(l+r)>>1; tr[2*p]+=(mid-l+1)*lz[p]; tr[2*p+1]+=(r-mid)*lz[p]; lz[2*p]+=lz[p]; lz[2*p+1]+=lz[p]; lz[p]=0; return; } void change(ll p,ll l,ll r,ll x,ll y,ll z){ if(x>y) return; if(x<=l && r<=y){ tr[p]+=(r-l+1)*z; lz[p]+=z; return; } pushdown(p,l,r); ll mid=(l+r)>>1; if(y<=mid) change(2*p,l,mid,x,y,z); else if(x>=mid+1) change(2*p+1,mid+1,r,x,y,z); else{ change(2*p,l,mid,x,mid,z); change(2*p+1,mid+1,r,mid+1,y,z); } update(p); return; } ll searchh(ll p,ll l,ll r,ll x,ll y){ if(x>y) return 0; if(l==x && r==y) return tr[p]; pushdown(p,l,r); ll mid=(l+r)>>1; if(y<=mid) return searchh(2*p,l,mid,x,y); else if(x>=mid+1) return searchh(2*p+1,mid+1,r,x,y); else return searchh(2*p,l,mid,x,mid)+searchh(2*p+1,mid+1,r,mid+1,y); } int main(){ scanf("%lld",&n); for(ll i=1;i<=n;i++){ scanf("%lld",&a[i]); pre[i]=pos[a[i]]; pos[a[i]]=i; } for(ll i=1;i<=n;i++){ ans+=searchh(1,1,n,pre[i]+1,i-1); if(pre[i]){ change(1,1,n,pre[i],pre[i],-searchh(1,1,n,pre[i],pre[i])); change(1,1,n,pre[i]+1,i-1,1); } else change(1,1,n,1,i-1,1); if(pre[pre[i]]) change(1,1,n,pre[pre[i]]+1,pre[i]-1,-1); else if(pre[i]) change(1,1,n,1,pre[i]-1,-1); // cout<<searchh(1,1,n,pre[i]+1,i-1)<<endl; // for(ll j=1;j<=n;j++){ // cout<<searchh(1,1,n,j,j)<<" "; // } // cout<<endl; } printf("%lld",ans); return 0; } ``` 过不了样例
最新发布
08-12
评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值