首页 > 代码库 > 重读TCPL:从汇编语句浅析++i和i++
重读TCPL:从汇编语句浅析++i和i++
TCPL(The C Programming Language)对自增和自减运算符的叙述并不多。在中文版的12页(英文版18页)和37页(英文版46页)中,只是简单地标明TCPL这本书会同意使用前缀形式,并用一段话解释了两者对值和值+1先后的区别。
之所以纠结这个,是因为用C语言写单片机的程序的时候,有的老师会强调++i和i++在使用的时候没有区别。那这这两个语句在汇编层到底是怎样的呢?
用简单的几行代码测试了一下,代码如下:
int main(){ int a = 0, c = 0; c = a++; c = ++a; printf("%d", a); for (; a < 4; a++) printf(" "); for (; a < 6; ++a) printf(" "); getchar(); return 0;}
++a和a++的汇编显示:
int a = 0;0027179E mov dword ptr [a],0a++;002717A5 mov eax,dword ptr [a]002717A8 add eax,1002717AB mov dword ptr [a],eax++a;002717AE mov eax,dword ptr [a]002717B1 add eax,1002717B4 mov dword ptr [a],eax
其中只要是单独出现的a++和++a。那么,他们的汇编语句就是一样的。不但行数一样,顺序也是相同的。所以前置语句和后置语句在单独使用,不赋值给其他变量的情况下不会出现有什么不同。老师说的话,估计就是看准了C语言控制单片机的相当一部分程序只有循环才会用到自增自减,才会这么说。
不同出现在赋值语句上,前置和后置在汇编层的顺序不同,如下:
int a = 0, c = 0;000F179E mov dword ptr [a],0000F17A5 mov dword ptr [c],0c = a++;000F17AC mov eax,dword ptr [a]000F17AF mov dword ptr [c],eax000F17B2 mov ecx,dword ptr [a]000F17B5 add ecx,1000F17B8 mov dword ptr [a],ecxc = ++a;000F17BB mov eax,dword ptr [a]000F17BE add eax,1000F17C1 mov dword ptr [a],eax000F17C4 mov ecx,dword ptr [a]000F17C7 mov dword ptr [c],ecx
可仍然能看到,不管是前置语句,还是后置语句,在汇编层语句的数量上是一样的,那么占用的空间和时间相同。但在网上经常搜到的前置语句和后置语句的效率问题又是什么导致的呢?
效率问题一般出在C++上,我去翻了C++ Primer后,发现是运算符重载的问题。书上的示例代码和网上找到的并不一样。网上对效率问题的解释是:后置++需要有临时对象才可以完成重载。
在第五版C++ Primer的502页(英文版566页)上则是:
class StrBlobPtr{public:strBlobPtr& operator++();};StrBlobPtr& StrBlobPtr::operator++(){check(curr,”increment past end of StrBlobPtr”);++curr;return *this;}class StrBlobPtr{public:strBlobPtr& operator++(int);};StrBlobPtr& StrBlobPtr::operator++(){StrBlobPtr ret = *this;++ *this;return ret;}
而且书上强调了“定义自增自减的运算符的类,需要同时定义前置和后置两个版本。”所以效率问题应该是仅对比两者的定义。
前置中多了函数检验,但从传递和值的返回上来看,后置返回的形式是一个值而非引用。而且因为要解决普通重载形式无法区分两种情况(因为使用的是同一个符号),所以后置版本需要额外接受一个int形参(不过,这个仅仅是为了区分,并非真的参与运算)。这两个重载在汇编层面的语句就切切实实的分开了,于是后置在某些场景中的使用速度比前置要低。
重读TCPL:从汇编语句浅析++i和i++