在把 Windows 程序移植到 Linux 時遇到了死循環,最後定位到了相似這種的語句 for (i = 0; i < 1; i = i++),linux
別問我是誰寫的,爲何這麼寫(淚目!)。緩存
根據我本身的感受, i = i++ 應該等價於 i++(C標準中 i=i++ 的行爲未定義), Windows 上確實是這樣,但 Linux 不是,這應該是編譯器差別形成的。spa
--------------------------------------- 可 i 的分割線 No.0 --------------------------------------------------------------
orm
那麼問題來了,爲何會這樣呢?內存
用 arm-linux-gcc -S i.c 彙編一下,編譯結果和源碼以下(gcc 彙編的代碼不太好看,因此用了 arm 版的):編譯器
首先介紹一下出現的彙編指令:源碼
mov r3, #0 (把常數值賦值給 r3,至關於 r3=0)it
str r3, [fp, #-8] (把 r3 的值存儲到內存地址 [fp, #-8])編譯
ldr r3, [fp, #-8] (把內存地址 [fp, #-8] 的值載入到 r3,和 str 相反)效率
add r2, r3, #1 (r2 = r3 + 1)
okey, 實際上是先把 i 的原始值緩存到 r3,而後加1的值賦給 r2,r2會更新 i 值(由於 i++), 最後 r3 也會更新 i 值(由於 i=),至於爲什麼是這個順序,請呼叫大神吧。因此,i 的值會一直爲0,致使文章開頭的 for 循環就死了。
--------------------------------------- 可 i 的分割線 No.1 --------------------------------------------------------------
爲了好理解,我還彙編出了 j=i++,以下:
j = i++ : 仍是先緩存 i 的原始值,而後把加1的 i 值賦給 i,最後在把緩存的 i 的原始值賦給 j(和 i = i++ 的順序同樣)。
j = ++i : 先更把加1的 i 值賦給 i,而後再取出 i 值賦給 j 。
--------------------------------------- 可 i 的分割線 No.2 --------------------------------------------------------------
此外,一直想看看i++ 和++i 的彙編有什麼不一樣,結果以下:
由於一直都聽過這個說法,for 循環中 ++i 的效率比 i++ 高,但從上圖中能夠當作,在單獨的語句中,
++i 和 i++ 是同樣的(gcc 系列編譯器),不過仍是推薦用 ++i,爲了移植性,不能相信編譯器。