造冰箱的大熊貓@cnblogs 2019/8/3
1、問題
某天寫了如下代碼:
unsigned char ReadByteFromFile ( FILE * fp ) { unsigned char ch; ... fread ( &ch, 1, 1, fp ); ... return ch; } void main() { ... printf ( "first byte = 0x%02x, second byte = 0x%02x\n", ReadByteFromFile ( fp ), ReadByteFromFile ( fp ) ); ... }
printf所在行的代碼本意是從文件中連續讀兩個字節並打印出來。假設被讀取文件的內容為“0x01 02 03 04 ... ...”,那么預期的運行結果是:
first byte = 0x01, second byte = 0x02
但實際運行結果(Ubuntu,gcc編譯)卻顛倒了個:
first byte = 0x02, second byte = 0x01
2、解答
嗯嗯,有意思。回想了很久以前上課內容並上網搜索一番,發現C標准里沒有規定編譯器在計算函數參數的次序(This form of argument-passing is known as call by value. The standard does not specify any order for the
evaluation of the arguments.)。也就是說,原想着printf()在運行時按照從左向右的順序計算參數值,在這里也就順序讀取了文件中的兩個字節。但實際上,編譯器輸出的結果卻是printf()函數按照從右向左的次序計算參數,這就導致了printf()中第一個ReadByteFromFile()函數(從左向右數)后讀取文件,而第二個ReadByteFromFile()卻先讀取文件,最終輸出結果與預想的次序顛倒。
或者用Stackoverflow上某個用戶提出的問題更好地說明這一問題:為什么下面代碼輸出結果是“4 5 5 4 5”。
main()
{
int i = 5; printf ( "%d %d %d %d %d %d", i++, i--, ++i, --i, i); }
因此,在使用函數中如果涉及對同一變量/對象的多次操作,一定要考慮到編譯器在處理函數參數計算時次序的不確定性。建議遇到這種情況時,還是現在函數外完成計算,再將計算結果傳遞給printf()。當然,如果能夠約定編譯器中參數計算次序(最好從左向右,與日常習慣相符),還是能省些事情,讓代碼看起來/寫起來簡潔一些。
2019.8.5補充:現在回想,好像當年上課的時候有過講授這方面的知識還有對應的考題,但真的太久遠了都忘記了。