C++汇编剖析 i++和++i本质
本篇用于讨论目前对i++和++i的主流看法:
1.++i比i++性能更高
2.i++是先用临时对象保存原来的对象,然后对原对象自增,再返回临时对象;++i由于不用生成临时变量,且以引用方式返回
3.sizeof函数
上面的几个说法在网上流传的挺广的,接下来,我们去剖析一下。
环境:Win10+x86
纯自增情况
int i = 0; i= sizeof(i); i++; ++i;
汇编代码:(保留了字符号,更方便观看,不使用字符好会更直观)
69: int i = 0; mov dword ptr [i],0 //0的前四个字节存到i的地址 70: i= sizeof(i); mov dword ptr [i],4 //使用4替换sizeof,存入i这个地址 71: i++; mov eax,dword ptr [i] //eax=i add eax,1 //eax=eax+1 mov dword ptr [i],eax //i=eax 72: ++i; mov eax,dword ptr [i] //eax=i add eax,1 //eax=eax+1 mov dword ptr [i],eax //i=eax
ptr:pointer(指针)的缩写,当两个操作数的宽度不一样时使用,可以理解为临时类型转换。
当我们调用sizeof的时候,会发现直接把4这个值传入。看我往期分析汇编会发现,在调用函数的时候,会有call xxxx地址这样的汇编,这样才是在调用一个函数。
sizeof用于获取当前平台当前变量的内存占用,在跨平台开发的时候有一个更安全的关键字,我忘记在那本书上看到过的了,搜也没搜到,后面补一下。TODO
目前我知道的比较对的有两种:
1.有人说这是编译器特性,当使用到的时候会用指定的值去替代
2.是一个单目操作符
我是觉得第一种更有可能,如果是一个操作符,应该会有类似add这样的操作指令,然而,这里是直接替换,没有任何多余的操作。
当我们在单纯自增的时候会发现他们的汇编都是一样的,在这种情况下,会有性能的差距吗?
参与运算情况
int i = 0; i= sizeof(i); int a= i++; int b= ++i;
汇编:
69: int i = 0;
mov dword ptr [i],0
70: i= sizeof(i);
mov dword ptr [i],4
71: int a= i++;
mov eax,dword ptr [i] //eax=i
mov dword ptr [a],eax //a=eax
mov ecx,dword ptr [i] //ecx=i
add ecx,1 //ecx=ecx+1
mov dword ptr [i],ecx //i=ecx
72: int b= ++i;
mov eax,dword ptr [i] //eax=i
add eax,1 //eax=eax+1
mov dword ptr [i],eax //i=eax
mov ecx,dword ptr [i] //ecx=1
mov dword ptr [b],ecx //b=ecx
在这种情况下只是换了个寄存器去自增,顺序有出入,但是实际执行的汇编语句还是那些。
int i = 0; i= sizeof(i); i++=3; ++i=3;d
当我们使用这段代码的时候,i++=3会报错,很多人会以左值和右值来解释这种情况,我也不知道哪个解释对不对,但是总感觉差那么点意思,现在我们从汇编角度出发。
我们写点汇编:
mov eax,dword ptr 3 //eax=3 因为要先赋值后运算 mov dword ptr [i],eax //i=eax=3
mov ecx,dword ptr [i] //ecx=i add ecx,1 //ecx=ecx+1 mov dword ptr [i],ecx //i=ecx
这一段大概就是i++=3的汇编,如果i++=3可以被编译的话。
先赋值会产出一个寄存器,然后用寄存器去进行后面的自增操作,我们观察把3赋值后产出的对象,i++赋值产出的是寄存器,++i产出的是i这个变量。
这里会产生一些二义性,本来我需要让i值去自增,结果变成了3的自增,而且,这样做仅仅只是做了一个运算而已,没有任何意义,没有任何产出。
72: ++i=3;
mov eax,dword ptr [i]
add eax,1
mov dword ptr [i],eax
mov dword ptr [i],3
73: i = ++i;
mov eax,dword ptr [i]
add eax,1
mov dword ptr [i],eax
mov ecx,dword ptr [i]
mov dword ptr [i],ecx
74: ++i;
mov eax,dword ptr [i]
add eax,1
mov dword ptr [i],eaxc
成为对象成员情况
class AA
{
int a=4;
public:
void operator ++();
void operator ++(int);
};
void AA::operator++() {
a++;
}
void AA::operator++(int) {
++a;
}
汇编:
21: ++a; mov eax,dword ptr [this] mov ecx,dword ptr [eax] add ecx,1 mov edx,dword ptr [this] mov dword ptr [edx],ecx
18: a++; mov eax,dword ptr [this] mov ecx,dword ptr [eax] add ecx,1 mov edx,dword ptr [this] mov dword ptr [edx],ecx
从头至尾,根本不会产生任何临时变量,只会有寄存器在操作值。
我在查相关反面教材的时,看到有人用汇编去分析++i和i++说“答案是两者效率在某些情况下仍有不同!当我们考虑自定义类的时候,就不一样了。”然后阐述最上面第2条舆论。使用汇编看本质,已经答案贴脸了,还说出这样的错误结论。
我们在运行过程中程序绝对不会夹带私货,去增加变量。

浙公网安备 33010602011771号