1. 程式人生 > >關於i++與++i的學習討論!

關於i++與++i的學習討論!

tor 結果 知識點 dword asm i++ span -c ofo

先談容易的知識點

  區別兩個

      1、 i++ 返回原來的值,++i 返回加1後的值。
      2、 i++ 不能作為左值,而++i 可以。

重點說下第二點。
首先解釋下什麽是左值

左值是對應內存中有確定存儲地址的對象的表達式的值,而右值是所有不是左值的表達式的值。

  左值與右值的根本區別在於是否允許取地址&運算符獲得對應的內存地址。

比如,
int i = 0;
int *p1 = &(++i); //正確
int *p2 = &(i++); //錯誤

++i = 1; //正確
i++ = 5; //錯誤

為什麽(i++)不能做左值,而(++i)可以
// 前綴形式:
int& int::operator++() //這裏返回的是一個引用形式,就是說函數返回值也可以作為一個左值使用 {//函數本身無參,意味著是在自身空間內增加1的 *this += 1; // 增加 return *this; // 取回值 } ==================================================================================================================================== //後綴形式: const int int::operator++(int) //函數返回值是一個非左值型的,與前綴形式的差別所在。 {//函數帶參,說明有另外的空間開辟 int oldValue = *this; // 取回值 ++(*this); // 增加 return oldValue; // 返回被取回的值 }
如上所示,i++ 最後返回的是一個臨時變量,而臨時變量是右值。so不能返回引用 。 why! 自己想! 關於原子性的討論繼續!!!!!!!!!

  都不是原子操作

1.i++分為三個階段:

內存到寄存器 寄存器自增 寫回內存

這三個階段中間都可以被中斷分離開.

2.++i首先要看編譯器是怎麽編譯的,

某些編譯器比如VC在非優化版本中會編譯為以下匯編代碼:

__asm
{
mov eax, dword ptr[i]
inc eax
mov dword ptr[i], eax
}
這種情況下,必定不是原子操作,不加鎖互斥是不行的。


假設加了優化參數,那麽是否一定會編譯為“inc dword ptr[i]”呢?答案是否定的,這要看編譯器心情,如果++i的結果還要被使用的話,那麽一定不會被編譯為“inc dword ptr[i]”的形式。
那麽假設如果編譯成了“inc dword ptr[i]”,這是原子操作,是否就不需要加鎖了呢?如果在單核機器上,不加鎖不會有問題,但到了多核機器上,這個不加鎖同樣會帶來嚴重後果,兩個CPU可以同時執行inc指令,但是兩個執行以後,卻可能出現只自加了一次。
真正可以確保不“額外”加鎖的匯編指令是“lock inc dword ptr[i]”,lock前綴可以暫時鎖住總線,這時候其他CPU是無法訪問相應數據的。但是目前沒有任何一個編譯器會將++int編譯為這種形式。

在多核的機器上,cpu在讀取內存i時也會可能發生同時讀取到同一值,這就導致兩次自增,實際只增加了一次。

關於i++與++i的學習討論!