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条舆论。使用汇编看本质,已经答案贴脸了,还说出这样的错误结论。

我们在运行过程中程序绝对不会夹带私货,去增加变量。

posted @ 2022-08-09 11:30  过往云烟吧  阅读(215)  评论(0)    收藏  举报