C語言在作算術運算時會遵守先乘除後加減後加減的基本規則,而各種運算元也都有相對應的優先權(某些運算元的優先權並不符合直覺,因此對於不熟悉的運算盡量加大括號來限制運算順序)
若非一般的運算,某些執行順序是屬於未定義的(undefined)
賦值順序
1 | int a; |
此段程式只有賦值運算,因此無法從優先權判斷優先順序,必須從關聯性(Associativity)判斷,而賦值是右關聯性(right associativity),也就是c會先賦值給b再賦值給a,因此a最終為2
為了符合正常使用,一般的 + - * / 運算皆為左關聯性(left associativity)運算,但像上述例子又是右關聯性運算,容易造成混淆,較好的作法則是將非一般使用的情況可以分開寫或加上大括號,以避免因為錯誤認知關聯性造成運算結果不如預期,例如上例應改為b = c; a = b;
較佳
函式順序
1 | x = f() + g() * h(); |
此運算會將g()和h()的回傳值相乘之後再加上f()的回傳值。但是g()和h()的執行順序則是未定義,可能g()先執行之後再執行h(),也可能h()先執行之後再執行g(),編譯器可根據狀況任意決定執行順序,若是這兩個函式皆會更改某一個全域變數,且根據此全域變數來決定回傳值的話,那麼執行順序不同會得到不同的結果
函式參數順序
1 | int a[] = {1, 2, 3}; |
此段程式碼的 *(pa), *(pa++), *(++pa)的執行順序是未定義的,因此在不同的編譯器上有可能得到不同的結果,我們在同一部x86機器上先用VS2010編譯且執行得到以下結果
接著以同樣的機器在ubutu下使用gcc編譯可得到以下結果
由此可見非定義行為將會由compiler決定,大部分的未定義行為可視為coding上的錯誤