變化簡單的C代碼:不同的結果
#include <stdio.h>
int main() {
int arr[] = {1, 2, 3, 4, 5};
int *ptr = arr;
printf("%d, %d\n", *ptr, *(++ptr));
return 0;
}
編譯用gcc 4.8.2,結果是:
2, 2
編譯鐺3.4,結果是:
1, 2
爲什麼會發生這種情況?
變化簡單的C代碼:不同的結果
#include <stdio.h>
int main() {
int arr[] = {1, 2, 3, 4, 5};
int *ptr = arr;
printf("%d, %d\n", *ptr, *(++ptr));
return 0;
}
編譯用gcc 4.8.2,結果是:
2, 2
編譯鐺3.4,結果是:
1, 2
爲什麼會發生這種情況?
調用函數時使用的逗號不包含sequence point。
因此,該代碼*ptr, *(++ptr)
調用未定義行爲,因爲它試圖訪問兩次PTR序列點之間,用於其他目的,而不是確定要分配什麼值PTR。
這是通過C11 6.5/2中定義,在下面的亂碼文本:
如果一個標量對象上的副作用是相對於任一 同一標物體上的不同的副作用或一個未測序值 使用相同標量對象的值計算,行爲是未定義的 。如果子表達式的子表達式有多個允許的排序順序,那麼如果在任何排序中出現這種不確定的副作用,則行爲是不確定的。
在我們的情況下,副作用(在*(++ptr)
改變PTR與++)相對於相同的對象(*ptr
)的值計算是未測序。
由於它是未定義的行爲,任何事情都可能發生。既然你的程序做了「某事」,它的行爲就像預期的一樣(或者說,就像「意外的」)。
此外,函數參數的評估順序未指定,因此無法知道函數調用中的第一個或第二個參數是先評估的。順序不僅可以在編譯器之間不同,而且可以在同一程序中的源代碼行之間有所不同。編譯器可以按照它喜歡的任何順序對它們進行評估,並且它不需要記錄如何。
+1用於調用C標準的「亂碼文本」。這是真的。 –
@mic_e C99中的文本是完全正確的:''在前一個和下一個序列點之間,對象的存儲值 最多隻能通過評估一個表達式進行修改。此外,先驗值應該只讀以確定要存儲的值「。在C11中,他們將其重寫爲上面那些不可讀的垃圾。 – Lundin
試試這個:
#include <stdio.h>
int main() {
int arr[] = {1, 2, 3, 4, 5};
int *ptr = arr;
printf("%d, %d\n", ptr[0], ptr[1]);
/* Or this
printf("%d, %d\n", ptr[1], ptr[0]);
whichever you meant
*/
return 0;
}
沒有爲函數參數的計算順序,編譯器可以選擇任何他們喜歡的定義的順序。我可以先執行*(++ptr)
,然後執行*ptr
,這會導致將參數2, 2
傳遞給printf,或者反過來導致1, 2
傳遞給printf。
而且clang實際上有一個'-Wunsequenced'的警告,它是'-Wall'的一部分。第零個錯誤正在發展,甚至沒有基本的警告。 –
這並不能完全回答這個問題,因爲結果不僅僅依賴於評估的順序,還會調用未定義的行爲。 – Lundin
printf行中的評估順序未指定,單獨足以允許兩種解釋。
另外,兩個指針訪問之間沒有順序點。這會導致未定義的行爲(請參閱:您的程序可能會崩潰,按預期工作,或將硬盤內容上傳到互聯網,具體取決於當前的星期幾以及您使用的編譯器)。
如果有警告編譯,你會發現以下幾點:
[email protected] $ gcc test.c -std=c11 -Wall -Wextra -pedantic
test.c: In function ‘main’:
test.c:6:29: warning: operation on ‘ptr’ may be undefined [-Wsequence-point]
printf("%d, %d\n", *ptr, *(++ptr));
^
[email protected] $ clang test.c -std=c11 -Wall -Wextra -pedantic
test.c:6:29: warning: unsequenced modification and access to 'ptr' [-Wunsequenced]
printf("%d, %d\n", *ptr, *(++ptr));
~~~ ^
1 warning generated.
生活提示:請使用-Wall -Wextra -pedantic
始終編譯,總是解決所有警告,常常覺得既clang
和gcc
測試,你就會有很少有錯誤。我甚至將-Werror
添加到我的發佈版本配置中。
序列點和未定義的行爲? – odedsh
這是未定義的行爲,看到這個[所以關於序列點的問題](http://stackoverflow.com/questions/4176328/undefined-behavior-and-sequence-points) –
因爲'* ptr,*(++ ptr)''讓你的程序在火上點燃頭髮。 – DevSolar