printf("%s", NULL) 和 printf("%s ", NULL) 的區別?
printf("%s
跟printf("%s", (char*)NULL); fflush(stdout); 有什麼區別?
", (char*)NULL)為什麼前者會導致Segmentation fault
後者列印(null)
因為:
- glibc 的 printf() 考慮了 %s 的參數是 NULL 的情況。
- gcc 編譯器會把 printf("%s
", x) 改寫為 puts(x),因為後者不需要 parse format string。 - 但是 puts() 沒有考慮參數是 NULL 的情況。
因此第一種寫法會 coredump,第二種不會。
這是一類月經問題。
無論 C / C++,都要求 printf 的 %s 對應一個有效的指向字元數組的指針。如果提供的參數與要求不服,則對應的行為未定義。
If any argument is not the correct type for the corresponding conversion specification, the behavior is undefined.
所以 printf 考慮了 %s 的參數是 NULL 的情況嗎?沒有,標準只規定其行為未定義。
無論格式化字元串寫成什麼樣子,只要你為其中的 %s 對應一個 NULL,列印「(null)」、列印「你這個笨蛋」、段錯誤、刪除磁碟上所有文件、幫你叫個外賣……都是合理合法的 C / C++ 程序的行為。
所以總結回答一下問題:由於是未定義行為,它們可能有區別,也可能沒有區別。
---
月經回答:通過彙編來解釋 C / C++ 的行為,和盲人摸象沒有區別。
更新:的確,彙編並不是有效的的方法,只能看到部分編譯器的現象,要真正理解c的行為還是要啃標準。
~~~~~~~~~最騷的分割線~~~~~~~~~~剛剛在知乎找到這個問題,然後發表了自己的結果,然後就在stackoverflow找到了答案,不得不說stackoverflow真是很厲害,至少每一個程序員都應該懂得利用這個網站(還有Google[順便吐槽一下百度搜索出來都是什麼鬼無聊東西,還是趁早退百度保平安])首先,我們分別編譯兩個文件
#include &
int main(void){
printf("%s", (char *)NULL);
}
#include &
int main(void){
printf("%s
", (char *)NULL);
}
通過附加-save-temps選項來查看彙編代碼
這是"%s"的"的可以發現,"%s"這個是調用了printf函數進行處理,就像 @陳碩 所說的printf把這個當成了一種情況來處理,識別到這是一個NULL指針,而"%s
"這個則是直接調用了puts函數來進行輸出,對於NULL指針並沒有作判斷處理,所以才會引發Segmentation fault。
而這個又正好解決了我的疑問,為什麼再"%s
"在任何位置加一點東西又不會引發Segmentation fault, 原因在於這次put又不足以解析這一段字元串,把他交給了printf來處理
以下附答案連接
What is the behavior of printing NULL with printf"s %s specifier?
printf("%s
",str); gives segmentation fault but printf("%s",str); don"t, where "str" is a string pointer
---原答案----
我也遇到這個問題printf("%s1
", (char *)NULL);
printf("1%s
", (char *)NULL);
printf("%s
1", (char *)NULL);
printf("%s", (char *)NULL);
printf("%s
1", (char *)NULL);
printf("%s ", (char *)NULL);
printf("%s
", (char *)NULL);
這麼多,就是最後一個會Segmentation fault
但是只要稍微改一下就不會出現,只有這個特定的格式才會出現,這是什麼BUG首先拋出vs2015:
沒有段錯誤,代碼如下:
加入"x00"進行對照,如果真的變為puts,"x00"由於是正經的字元串不會崩潰,NULL則會讓puts發生段錯誤,然後idapro反彙編結果如下:
調用了四次printf,可見在VS中二者寫法並沒有區別;
gcc結果如下,先貼代碼:
其中如果加入最後一個printf,會崩潰的,注釋掉最後一個printf編譯成功得到的二進位彙編如下:
它優化的是%s
,而%s反而沒有優化。
對於最後一個printf,會被優化為puts(NULL),顯然不合法,發生段錯誤
然而printf(NULL)是合法的,輸出為(NULL)
如上
vs2015測試,沒有錯誤。
推薦閱讀:
※現代編程語言需要具備什麼要素?
※int 和 long int 的區別在哪裡?
※VR需要掌握什麼編程語言?
※有哪些比較好的關於編譯原理 ,操作系統的網路公開課?