2016-03-05 41 views
-4

下面的代碼在Code :: Blocks上編譯和運行時顯示不同的結果。初學者關於C程序的查詢函數調用堆棧,序列點(排序)

void sum(int a,int b){ 
    printf("a=%d b=%d\n",a,b); 
} 
int main(){ 
    int i=1; 
    sum(i=5,++i); 
    printf("i=%d\n\n",i); 
    /***********************/ 
    i=2; 
    sum(i=5,i++); 
    printf("i=%d\n\n",i); 
    /**********************/ 
    i=3; 
    sum(i=5,i); 
    printf("i=%d\n\n",i); 
    return 0; 
} 

輸出:

a=5 b=5 
i=5 

a=5 b=2 
i=5 

a=5 b=5 
i=5 

我認爲這個問題的答案關係到序列點,並在此++運算符的序列點有關。 GCC必須按照命令傳遞值以固定順序堆棧,但由於++的不同,答案不同。我覺得對於初學者來說這樣寫一個函數調用並不常見,但關於運算符的教訓是一般的,所以可以嘗試。

我的問題是,它的確切答案和像這樣的問題應該是什麼?在編譯的哪個階段,這些事情是決定的(明確或不清楚)?涉及哪種特定的算法(用於優化或一般)?相同的編譯器能否爲這種表達式或語句提供不同的結果最後一個問題是,初學者將如何理解和解決這些問題?有時非常令人驚訝。

+0

可能的重複[爲什麼這些構造(使用++)未定義的行爲?](http://stackoverflow.com/questions/949433/why-are-these-constructs-using-undefined-behavior) – Olaf

+1

逗號在'sum(i = 5,++ i)中;'不是序列點,它分離函數參數。教訓是,如果你想使用從單個變量派生的不同值,例如使用'sum(i + 5,i + 1)'並在之後更新'i'。或者在這種情況下,只需'sum(5,i + 1)'。除了別的,還不清楚你打算把「我」的最終價值是什麼。是'5'還是'6'或'2'? –

+0

是的,這是更好的寫作方式。 – skyconfusion

回答

1

操作的順序是在編譯的多個階段決定的,這是導致您看到奇怪結果的原因。特別是在優化階段,編譯器可以以不常見的方式對代碼進行重新排序,在這種情況下,它會影響結果(這很好,因爲你正在做一些未定義的事情,並且編譯器明確允許它做任何事情與該代碼)。沒有涉及任何具體算法,它是在不同點應用的幾種不同算法之間的交互,並且在每個點應用的算法可以根據編譯器決定的是處理特定位代碼的最佳方式而變化。

當文檔提到未定義的行爲時,它不是未定義的特定編譯器的行爲,而是編譯器必須或允許執行的規範。編譯器的行爲是完全定義的,但它是由深入分析器,代碼生成器和優化器模塊設計的詳細決策來定義的,而且它非常複雜,即使編寫編譯器的開發人員也不會告訴你它會做什麼,而無需花費大量的時間分析給定的代碼如何流經整個過程。

初學者將無法弄清楚結果。即使是專家開發人員也可能無法。這就是爲什麼「未定義」對開發人員來說這樣一個不受歡迎的詞,爲什麼他們試圖避免像鼠疫這樣的不確定行爲。引用討論的語言規範,"In short, you can't use sizeof() on a structure whose elements haven't been defined, and if you do, demons may fly out of your nose."

+0

「編譯器的行爲已完全定義」 - 這不是必需的。編譯器可以很好地使用啓發式的隨機組件。鏈接(至少它的文本)意味着完全不同的東西。如果'struct'的大小不知道,就不可能計算它的大小。 – Olaf

+0

這是不可能計算的,但是因爲試圖做它的未定義行爲的結果,編譯器可以自由地做任何事情,從拋出一個錯誤到只是假定一些任意大小,並使用它來崩潰機器或更糟。拋出一個錯誤是因爲計算的不可能性只是最常見的行爲(因爲大多數編譯器開發者都是合理的,即使他們被允許也不會寫出惡意行爲)。 –