自增自减操作符在编程中很常用,都分为分为前置和后置两种操作符,对于两者的区别,C++ primer中有着如下说明:
1. 前置操作返回的结果为左值,后置操作返回的是右值,也就是说执行如下的代码都会编译不通过:
int i = 0, j = 0;
i++ = j;
cout << i++++ << endl << ++i--;
2. 前置操作符相比后置操作符所做的工作要少,就是对其操作是执行加1或减1操作,在原操作数上进行,也就是++i等价于i += 1; 后置操作符则必须先保存操作数原来的值,以便返回未加1之前的值作为操作的结果,因此,建议用前置操作符,对于int型对象和指针,编译器可优化掉这项额外工作,但是对于更多的复杂迭代器类型,这种额外的工作可能会花费更大的代价,因此,养成使用前置操作的习惯,就不必操心性能差异的问题了。
我对上述两点进行了测试,第一种很容易验证,但是第二点就出了些小问题,下面一一道来:
为了更深入地测试,我观察了汇编代码。
在codeblocks中进行测试,如下两行代码:
++i;
i--;
生成的汇编代码一致,都为incl 0xc(%esp),也就是单独写在一行时,编译器会进行优化,此时两者的效率是一样的。对于double,long等等内置类型进行测试,都会进行优化。这就验证了C++ Primer中所说,并没有网上一些人说的,后置操作符要多占一个临时空间,但毕竟这是两个单独的语句,进行进一步的测试,对于下面的代码:
int i = 3;
int j = 0;
j = ++i;
j = ++i;对应的汇编代码为incl 0xc(%esp)
mov 0xc(%esp), %eax
mov %eax, 0x8(%esp)
如果将j = ++i;换为j = i++;则对应的汇编代码为
mov 0xc(%esp), %eax
mov %eax, 0x8(%esp)
incl 0xc(%esp)
两者对比下,所做的工作基本一致,就是顺序不同,都要用到eax寄存器,没有出现前置比后置操作符要效率高的表现,也没有出现网上所说的额外的存储空间的问题。符合C++ Primer上所说的编译器会进行优化。编译器对内置类型会进行极大的优化,比如j = ++++i;对应的汇编代码为
incl 0xc(%esp)
incl 0xc(%esp)
mov 0xc(%esp),%eax
mov %eax,0x8(%esp)
直接进行两次自增,而不会先算出++i,然后再算++(++i)。
下面进行进一步的测试,对复杂的迭代器类型,这次编译器可能对优化有心无力了。因为迭代器生成的汇编代码有些复杂,所以换一种方式进行测试,代码如下:
clock_t start = 0, finish = 0, duration = 0;
vector<int> *v = new vector<int>();
v->assign(1000000, 2345);
vector<int>::iterator iter = v->begin();
//运行5000次
for (int i=0; i<5000; ++i)
{
start = clock();
for (; iter != v->end(); ++iter);
finish = clock();
duration += finish - start;
}
cout << "总运行时间为:" << duration << "毫秒" << endl;
最后运行结果:++iter的平均时间为:24毫秒iter++的平均时间为:31毫秒
基本上可以验证书中所说,所以平时要进行for循环时,养成用前置的习惯比较好