不要沒事挑戰i++和++i,比如x = x++;這種貨!

先上結論:

  1. x++和x+1不是一回事!
  2. 這個玩意沒有優先級!!!
  3. 這種寫法,是C標準嚴格禁止的。和伸手摸電門一樣,寫這種代碼屬于做死
  4. 關于這種寫法的結果的一切討論,都是無意義的。

一段代碼引出的糾結

先看如下一段代碼,猜猜看,輸出的是什么?

int a;
a = 0;
a = a++;
System.out.println("a = " + a);

最終輸出的是:a = 0

為什么是0而不是1?

查看一下字節碼,會發現++操作與+1不一樣。

我們平時總是說前綴表達式優先級高,后綴表達式優先級低,其實并不是,這里根本不存在優先級問題,而一定要說的話,++運算符的優先級高于賦值運算符!

在程序運行a++時,a的值首先是賦值給一個拷貝或者說臨時變量(按值傳遞,底層實現),即temp = a(即temp = a = 0),然后a執行自增運算(運算后a的值為1),最后將這個拷貝(此時拷貝的值為0)作為(a++)整體的值賦值給a(賦值后a的值有重新從1變為0),所以最終的a的值輸出為0。

即a = a++;語句等價于:

a=(temp=a,a+=1,temp);

或者我們干脆這么理解:

int b = a++;
a = b;

那么x = x++;算什么?

這在c標準里,這種操作稱為未定義行為。

C99中:
J.2 Unde?ned behavior
Between two sequence an object is modi?ed more than once, or is modi?ed and the prior value is read other than to determine the value to be stored (6.5).

大致意為在兩個序列點(;或,)中,同一變量被修改超過一次的做法是未定義行為。
C99還規定,未定義行為由編譯器自行處理,輸出什么結果都可以!輸出一個Hello World都是符合標準的。

x++確切的解釋

i = i + i++之類問題,根本不是優先級的問題。

簡單來說,a = b,和數學課本上的等式完全是兩個意思。在計算機領域,它的意思是:先計算出表達式b的值,然后把這個值賦給a。

表達式的定義為:一個單獨的字面值,或者一個單獨的變量,或者通過算術/邏輯運算甚至函數調用連接起來的表達式——注意,賦值操作不是什么算術/邏輯運算,也不是函數調用。

顯然,對于表達式b來說,它的運算符優先級有多復雜都不是問題。

但,因為太重要所以需要再說一遍:請注意,表達式里面不允許出現賦值操作,因為這個操作并不是算術/邏輯運算。

顯然,i++的問題在于,雖然i++看起來只有操作符和操作數組合、而且通常作為表達式使用,但其實它的含義是i=i+1——這根本不是一個表達式,而是“計算表達式i+1的值,并將其賦予變量i”:換句話說,這里面額外有一個賦值操作。

事實上,i++本身作為一個c/c++語句,是不可刪除的;而 2+3、a&&b、!a之類真正的表達式構成的單獨語句則可以在編譯時直接刪除。原因就是i++另外還隱含了一個賦值操作,從而多了個會影響程序狀態的“副作用”。

c/c++里面,類似這個賦值操作的、執行后會影響程序狀態的行為,被稱為“副作用(side effect)”。

進一步的,c/c++標準里面對這類有表達式外表、但卻另有額外語義的“假”表達式叫做“有side effect的表達式”(關于何謂side effect,c/c++標準有專門定義,請盡量參考這個定義,因為我的轉述很可能會有某些瑕疵之處,不可輕信),實質上也是強調了它和原始意義上的表達式的不同之處。

但是呢,為了寫代碼的便利,c/c++系語言提供了一個語法糖,允許程序員將i++用到表達式里面,同時規定其含義為:首先取i的值,用這個值代入表達式,供以后求值用;之后,執行i=i+1(執行i=i+1的確切時機不限,在表達式求值之前還是之后都行,只要執行了就對)。

如此一來,忽略副作用不提的話,i++看起來就像是一個真正的表達式。

但,必須注意,i++畢竟不是一個表達式,它畢竟還有個副作用藏在里面。粗暴的用某種規定允許它摻乎進去,就必然帶來很多棘手的問題。

比如說,i=i++,這個語句如何解釋?

首先,這顯然是一個賦值語句,所以最終i應該存的是等號右側表達式的值;雖然i++不是表達式,但按照規定,它可以解釋為“語句執行前i的取值”;所以,這其實是把語句執行前,i的取值賦給i的一個賦值語句——也就是說,執行后,i的值應該不變。

但,注意i++還有一個賦值動作。即:把語句執行前的i值加一,然后賦值給變量i——所以,執行后,i的值應該增加了1。

顯然,兩個賦值動作的執行結果出現了矛盾。究竟哪個對呢?

進一步的,i=(i++)+(i++)呢?這里面可有三個針對i的賦值操作啊。

不僅如此,對于函數調用,如max(i++, i++),這又是什么意義呢?

很顯然,不是表達式的i++,絕對不能和表達式混淆。

雖然,為了表達簡潔,c/c++系列語言允許它在特定場合代替表達式,但這并不等于說,c/c++就認為它和表達式沒有差別。相反,c/c++自始至終都認為它是一個賦值操作,只是可以在嚴格限定的場景替代表達式而已——這個“嚴格限定”,就是“不允許一個變量在一對序列點之間兩次改變其值”(不太嚴謹的說法)。

只有滿足了這個“嚴格限定”,程序才不會出現“二義”。

換句話說,i++本身是一個有著特定內涵(對i賦值)的指令,并不是單純的數學表達式。把它當基本數學表達式濫用,得到的復合表達式是沒有數學意義(因而也沒有現實意義)的。

把它用對,是程序員的責任。

最后給個面試題的例子,看看就好,畢竟真有人考這玩意……

public class Test {
    public static void main(String[] args) {
        test1();
        test2();
        test3();
        test4();
        test5();
        test6();
        test7();
    }

    private static void test1() {
        int a;
        a = 10;
        a = a++;
        System.out.println("test1 a = " + a);//10
    }

    private static void test2() {
        int a, b;
        a = 10;
        a = a + a++;
        System.out.println("test2 a = " + a);//20
    }

    private static void test3() {
        int a;
        a = 10;
        a = a++ + a;
        System.out.println("test3 a = " + a);//21
    }

    private static void test4() {
        int a;
        a = 10;
        a = a++ + ++a;
        System.out.println("test4 a = " + a); //22
    }


    private static void test5() {
        int a, b;
        a = 10;
        b = a + a++;
        System.out.println("test5 a = " + a);//11
        System.out.println("test5 b = " + b);//20
    }

    private static void test6() {

        int a, b;
        a = 10;
        b = a++ + a;
        System.out.println("test6 a = " + a);//11
        System.out.println("test6 b = " + b);//21
    }

    private static void test7() {
        int a, b;
        a = 10;
        b = a++ + ++a;
        System.out.println("test7 a = " + a);//12
        System.out.println("test7 b = " + b);//22
    }
}

參考了作者:invalid s在https://www.zhihu.com/question/23180989/answer/23874381中的描述。

?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 228,443評論 6 532
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 98,530評論 3 416
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 176,407評論 0 375
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 62,981評論 1 312
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 71,759評論 6 410
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 55,204評論 1 324
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,263評論 3 441
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,415評論 0 288
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 48,955評論 1 336
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 40,782評論 3 354
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 42,983評論 1 369
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,528評論 5 359
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,222評論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,650評論 0 26
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,892評論 1 286
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,675評論 3 392
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 47,967評論 2 374

推薦閱讀更多精彩內容