1. 程式人生 > >C/C++中自增自減的前置和後置區別

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中所說,並沒有網上一些人說的,後置操作符要多佔一個臨時空間,但畢竟這是兩個單獨的語句,進行進一步的測試,對於下面的程式碼:
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迴圈時,養成用前置的習慣比較好