[C語言] - 執行順序探討

C語言在作算術運算時會遵守先乘除後加減後加減的基本規則,而各種運算元也都有相對應的優先權(某些運算元的優先權並不符合直覺,因此對於不熟悉的運算盡量加大括號來限制運算順序)

若非一般的運算,某些執行順序是屬於未定義的(undefined)

賦值順序

1
2
3
int a;
int b = 1, c = 2;
a = b = c;

此段程式只有賦值運算,因此無法從優先權判斷優先順序,必須從關聯性(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
2
3
4
5
int a[] = {1, 2, 3};
int *pa;

pa = &a[0];
printf("a[0] = %d\ta[1] = %d\ta[2] = %d\n",*(pa), *(pa++),*(++pa));

此段程式碼的 *(pa), *(pa++), *(++pa)的執行順序是未定義的,因此在不同的編譯器上有可能得到不同的結果,我們在同一部x86機器上先用VS2010編譯且執行得到以下結果

接著以同樣的機器在ubutu下使用gcc編譯可得到以下結果

由此可見非定義行為將會由compiler決定,大部分的未定義行為可視為coding上的錯誤