為什麼說++i的效率比i++高?

  • 2019 年 10 月 4 日
  • 筆記

來源:公眾號【編程珠璣】

作者:守望先生

網站:https://www.yanbinghu.com

前言

不知道你是否聽說過++i比i++快的說法,真的如此嗎?

++i與i++的區別

這兩個表達式從我們初學程式語言的時候就會接觸到。前者是自增後取值,後者是取值後自增。 我們看一個簡單的例子。

#include <iostream>  using namespace std;  int main()  {      int a = 0;      int b = 0;      int c = a++;//int tmp = a;c=a;a = a + 1      int d = ++b;//b = b + 1;d = b;      cout<<"c="<<c<<";d="<<d<<endl;      return 0;  }  

運行結果:

c=0;d=1  

對於這個結果我們並不感到意外。

另外我們還注意到另外一個有意思的現象:

//來源:公眾號【編程珠璣】地址:https://www.yanbinghu.com  #include <iostream>  using namespace std;  int main()  {      int a = 0;      int b = 0;      int *c = &(a++);      int *d = &(++b);      return 0;  }  

編譯後報錯:

main.cpp:7:19: error: lvalue required as unary 『&』 operand       int *c = &(a++);  

說&作用於左值,也就是說a++的結果並非左值。但++b的結果是左值。

可簡單理解左值和右值:

  • 左值,有名對象,可賦值
  • 右值,臨時對象,不可被賦值

運算符重載

在《運算符重載》一文中已經說到了運算符的重載,通過前面的例子也發現了,對於內置類型,前置自增返回對象的引用,而後置自增返回對象的原值(但非左值)。 基於上述原則,一個前置版本和後置版本的常見實現如下:

//來源:公眾號【編程珠璣】地址:https://www.yanbinghu.com  class Test  {  public:      Test& operator++();//前置自增      const Test operator++(int);//後置自增  private:      int curPos; //當前位置  };  /*前置自增實現範式*/  Test& Test::operator++()  {      ++curPos;      //自增      return *this;  //取值  }  /*後置自增實現範式,為了與前置區分開,多了一個int參數,但從來沒用過*/  const Test Test::operator++(int)  {      Test tmp = *this;  //取值      ++curPos;             //自增      return tmp;  }  

仔細觀察後,我們發現前置自增,先自增,後返回原對象的對象;沒有產生任何臨時對象;而後置自增,先保存原對象,然後自增,最後返回該原臨時對象,那麼它就需要創建和銷毀,這樣一來,效率孰高孰低就很清楚了。

在不進行賦值的情況下,內置類型前置和後置自增的彙編都是一樣的呢!

void test()  {    int i = 0;    i++;    //++i;  }  

彙編:

push    rbp  mov     rbp, rsp  mov     DWORD PTR [rbp-4], 0  add     DWORD PTR [rbp-4], 1  nop  pop     rbp  ret  

不過,賦值的情況下,並且不開啟編譯器優化,它們的彙編程式碼還是有差別的,有興趣的可以試試。

總結

對於內置類型,前置和後置自增或者自減在編譯器優化的情況下,兩者並無多大差別,而對於自定義類型,如無特別需要,人們似乎更加偏愛前置自增或自減,因為後置自增常常會產生臨時對象。

但是,又能提高多少效率呢?