【C++奇技淫巧】前置自增与后置自增的区别(++i,i++)【2023.02.08】
简介
先说++i
和i++
的区别,判断语句中if(i++)
是拿i
的值先判断,而后自增;if(++i)
是先自增i
再进行判断。涉及到左值与右值也有点区别,i++
返回的是右值,++i
返回的是左值。也就是下面的代码要解释的东西。
#include <iostream>int main()
{//后置自增,返回右值int i = 0;auto pi = &(i++); 错误 右值无法取地址//前置自增,返回左值int j = 0;auto pj = &(++j);// 正确std::cout << "Hello World!\n";
}
转折
老生常谈的经验是++i
比i++
性能好,因为++i
中间会产生临时变量,参考这篇文章的解释http://t.csdn.cn/2A7PT。实际上较新的编译器,简单的自增没有差别了。我们看下汇编代码:
int i = 0;
00007FF73AD21C9B mov dword ptr [i],0 i++;
00007FF73AD21CA2 mov eax,dword ptr [i]
00007FF73AD21CA5 inc eax
00007FF73AD21CA7 mov dword ptr [i],eax int j = 0;
00007FF73AD21CAA mov dword ptr [j],0 ++j;
00007FF73AD21CB1 mov eax,dword ptr [j]
00007FF73AD21CB4 inc eax
00007FF73AD21CB6 mov dword ptr [j],eax
i++
与++j
的汇编代码一模一样,基本不涉及临时变量。
我们加上if
进行观察:
int i = 0;
00007FF7BEB91C9B mov dword ptr [i],0 if (i++)
00007FF7BEB91CA2 mov eax,dword ptr [i]
00007FF7BEB91CA5 mov dword ptr [rbp+0F4h],eax
00007FF7BEB91CAB mov eax,dword ptr [i]
00007FF7BEB91CAE inc eax
00007FF7BEB91CB0 mov dword ptr [i],eax {}//前置自增,返回左值int j = 0;
00007FF7BEB91CB3 mov dword ptr [j],0 if (++j)
00007FF7BEB91CBA mov eax,dword ptr [j]
00007FF7BEB91CBD inc eax
00007FF7BEB91CBF mov dword ptr [j],eax {}
注意:00007FF7BEB91CA5 mov dword ptr [rbp+0F4h],eax
这行代码是把i++
前的值拷贝到临时变量了,临时变量的地址是rbp+0F4h
。而后才对i
进行自增操作。
总结
也就是说编译器优化仅对简单的自增进行了优化,但是复杂的,甚至仅仅是套了一层if
,编译器是不会优化的,因为要兼容已有的代码。如果if
里面的也做优化,则之前那么多的程序岂不是要炸了💣?也就是语义上要保持连贯性,已经形成共识的东西编译器是会传承下去的。
再一个也是不理解既然汇编语言i++
和++i
都一样,为什么&i++
还是会报无法对右值取地址,其实也是编译器对这种复杂点的语句,不会对前置自增后置自增进行优化,生成汇编代码的时候还是对右值取的地址,导致的报错。