關於i++與++i的學習討論!
先談容易的知識點
區別兩個
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的學習討論!