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