C/C++中自增自減的前置和後置區別
自增自減操作符在程式設計中很常用,都分為分為前置和後置兩種操作符,對於兩者的區別,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中所說,並沒有網上一些人說的,後置操作符要多佔一個臨時空間,但畢竟這是兩個單獨的語句,進行進一步的測試,對於下面的程式碼:
j = ++i;對應的彙編程式碼為int i = 3; int j = 0; 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迴圈時,養成用前置的習慣比較好