探討 C# 編譯器對於 a++ 的處理方式
前一陣子在論壇上有人問了個很有趣的問題:『為什麼 int a=0; a = a++; 後,a 還是 0?』。
首先,我們來瞧瞧以下程式碼是如何被執行的:
static void Main(string[] args)
{
int a = 0;
a++;
}
- 記憶體裡會產生一個變數 a
- Push 一個 0 值給 Stack,此時 Stack 只有一個元素
- 將 Stack 頂層的值 Pop 出來,塞給 a 變數,此時 Stack 已經沒有任何元素
- (以上就是 int a = 0)
- 接著將 a 變數的值 Push 進 Stack,所以 Stack 有一個元素,值為 0 (也就是 a 變數的值)。
- 接著 Push 一個 1 值給 Stack,此時 Stack 有兩個元素
- 然後把 Stack 頂層的值,也就是 1,Pop 出來,此時 Stack 剩一個元素
- 再將 Stack 頂層的值,也就是 0,Pop 出來,此時 Stack 已經沒有任何元素
- 將 1 和 0 相加的結果 Push 回 Stack,所以 Stack 有一個元素,值為 1 (也就是相加的結果)
- 然後把 Stack 頂層的值,也就是 1,Pop 出來,塞給 a 變數
- 結束
以上的程序其實和以下的程式碼是一樣的:
static void Main(string[] args)
{
int a = 0;
a = a + 1;
}
接著來看看主題吧,下一段程式碼是怎麼執行的:
static void Main(string[] args)
{
int a = 0;
a = a++;
}
- 記憶體裡會產生一個變數 a
- Push 一個 0 值給 Stack,此時 Stack 只有一個元素
- 將 Stack 頂層的值 Pop 出來,塞給 a 變數,此時 Stack 已經沒有任何元素
- (以上就是 int a = 0)
- 接著將 a 變數的值 Push 進 Stack,所以 Stack 有一個元素,值為 0 (也就是 a 變數的值)。
- 有趣的地方在這裡,因為 a = a++ 會造成兩次回傳,一個是自己,一個是等號左邊的變數,所以此時會將 Stack 頂層的值 Pop 出來,然後複製這個值。
- 接著將剛剛的兩個值 Pop 進 Stack,所以此時 Stack 有兩個元素,內容值都是 0
- 接著 Push 一個 1 值給 Stack,此時 Stack 有三個元素,值分別為 1,0,0
- 然後把 Stack 頂層的值,也就是 1,Pop 出來,此時 Stack 剩兩個元素
- 再將 Stack 頂層的值,也就是 0,Pop 出來,此時 Stack 剩一個元素
- 將 1 和 0 相加的結果 Push 回 Stack,所以 Stack 有兩個元素,值為 1 (也就是相加的結果) 和 0
- 將 Stack 頂層的值 Pop 出來指派給 ++ 運算子前面的變數,因為程式碼是 a++,所以指派給 a,這個值是 1,STack 只剩一個元素 0
- 將 Stack 頂層的值 Pop 出來指派給 = 左邊的變數,因為程式碼是 a = ,所以很巧的也是指派給 a ,這個值是 0,Stack 清空
- 所以無論你怎麼看,除非你看得到 Stack 的流程,否則你永遠看到的 a 值都是 0
以上還是 C# 編譯器沒有使用最佳化編譯的狀態,如果是最佳化編譯,以上面那兩行程式碼,就只會做兩件事:
- Push 一個 0 值進入 Stack
- 把它 Pop 掉
- 沒了
這篇簡單地使用 Stack 的運算過程解釋一下為何 a = a++ 無論怎麼搞都是原來的值的原因,我覺得還滿有趣的。