當前位置: 華文星空 > 知識

i=1,為什麽 (++i)+(++i)=6?

2020-09-18知識

為沒有耐性看完的人

本題在C/C++中是未定義行為,可能有4,5,6等不同的答案。在其它程式語言中,取決於它如何規定的求值順序(order of evaluation),不是運算子優先級,見Java語言標準;Javascript語言標準;C#的語言標準;Python語言標準。

為什麽是4,5,6請跳到後面看。

關於這個程式碼的意義?

其一,不要用組譯推導規則。C/C++的規範中有明確說明,哪些是未定義行為,未指明行為,以及實作決定行為。這些都是組譯解釋不了的部份。去讀書,讀好書,實在不確認,再讀標準規範。理解不了,看看組譯,幫助理解。

其二,我這個答案想強調的是: 程式不是按順序執行,而是按規則執行。 大多數C/C++程式設計師,不要這麽寫。特別不要「想當然」的這麽寫。

其三,請出題的老師不要出這種題。出題了又解釋不清楚,那是打自己的臉。

其四,實踐中,這類求值順序不定的程式碼有實際意義,但這個過度簡化的程式碼沒有實際意義。

常見的程式碼 f() + g(),這個程式碼常見,它可能是UB,準確的說是未指明的行為(unspecified behavior)。為什麽呢,因為f()和g()的呼叫次序不確定。

UB是明確寫在語言規範中,」沒有定義的行為「本身卻是有定義的。

比如C#和Java中它都是5。那是因為C#和Java的語言規範中對這個求值順序是有定義,左運算元優先,在左運算元被求值以前,右運算元不會求值。但是,他們會有同樣的問題:

int i = 1; a[i] = (++i) + 1;

哪麽a[1] = ?

真正的解釋

本題(++i)+(++i)可能等於6,也可能不等於6。它的結果可能隨編譯器或平台的不同而不同。但在指定版本編譯器,指定平台情況下,它一般只有一個結果,這是可重現的。

括弧的優先級高,應該沒有疑問,所以題目分成三步

  1. ++i
  2. ++i
  3. (1) + (2)

這就帶來了一個聰明,但錯誤的理解:1步和2步是一樣的呀,它的順序不同不影響結果呀?

我們知道++i的等價於 b = a + 1 來看,這樣看就有幾個完全不同的理解了。這裏的a,b以及後面的c可以看成對i的過程值,在求值的過程中i的取值。比如 i 在求值前,它是值是a,求值後它的值是b。編譯器如何使用a,b,c決定了結果不同。

  1. b = a + 1
  2. c = b + 1
  3. b + c

b先求,c後求,但分別使用,結果是5( a = 1, b = 2, c = 3)

  1. b = a + 1
  2. c = a + 1
  3. c + b

b,c分別求值,但相互不影響,結果是4( a = 1, b = 2, c = 2)

  1. b = a + 1
  2. b = b + 1
  3. b + b

b先求,c後求,但c受b影響。c就是b,它們變成一個值,結果是6( a = 1, b = 3)

事實上,我們只有一個變量i=a,並沒有b和c。b,c就是副作用。本題不確定的原因是兩個副作用出現的次序在語意規則中沒有定義。如果我們規定 + 號左邊的副作用出現在右邊的副作用之前,那麽本題b先求,c後求,但c受b影響,最後得6。

C和C++語言中對於 副作用出現的規則,一般以語句或聲明的結束為標記 ,單看運算式,很多都是沒有定義。

從實作的角度,一般是參考操作符的結合性,左結合的左邊副作用先出,右結合的操作符右邊的副作用先出現, 或者反過來, 並不是隨心所欲。

編程是一個講道理的活動。