關於Visual Studio中printf(printf_s)函數用%n輸出值的問題
今天遇到件有趣的事。看到有人問以下代碼為什麼出錯:
scanf(...);
printf("%n...");
在下才疏學淺,也是剛開始學習,以前並沒有注意到輸入輸出函數還有個%n可以用,實在汗顏。就查詢了%n的含義:
將到此字符之前為止,一共輸出的字符個數保存到一個整型變量,不輸出文本。
意思差不多理解了,所以我給他的代碼加了一個指向整型變量的指針:
int val;
// ...
printf("%n...", &val);
一調試運行,還是報錯,選擇繼續調試,VS拋出異常:
Unhandled exception at 0x00007FF8A866765C (ucrtbased.dll) in Project1.exe: An invalid parameter was passed to a function that considers invalid parameters fatal.
我也看不懂,感覺就是參數有問題,又把&val改成val試試,萬一呢。
還是報錯。
我看來看去,感覺沒問題,又把網上人家給的實例代碼直接復制過去:
int i,j;
printf("joidg%nkdjdkjfj%n", &i, &j);
printf("%d,%d", i, j);
這下總沒問題了吧?
可惜,還是同樣的錯誤。
我有點懵逼,又想可能網上的解釋有問題吧,或許是%n本來只能用在scanf函數中(已經測試通過),但網上給出printf中用%n的例子不少,我不認為是偶然。
又查了一波權威一點的資料:
MSDN的解釋:
Type of input expected:No input read from stream or buffer.
Type of argument:Pointer to int, into which is stored number of characters successfully read from stream or buffer up to that point in current call to scanf functions or wscanf functions.
唔,這是scanf的參數說明,但沒有在printf的說明中找到相同的,因為人家printf函數壓根沒有特別解釋參數,就給了個“格式化文本”的模糊限定。
無奈,硬著頭皮又運行了一下代碼,這次把VS C++運行庫給的警告記下了,又去查這個錯誤說明(之前因為不能直接復制忽略了......)。
在MSDN官方網站查詢“n fomate specifer disabled”關鍵字,終於在某個帖子裡看到了相同問題。這是鏈接。
同時也終於找到printf函數的參數說明,裡面的確有%n的說明的。這是鏈接。
這下總算真相大白,原來是編譯器的問題,GNU的編譯器支持這種格式,而微軟的Visual C++不支持。
換了sublime用G++編譯試試,果然,代碼通了。
但是有個問題,我同時也用虛擬機試了Windows 7環境下VC++6.0,結果也能正常運行,原因何在?或許VC++6.0採用的標準和VS2015(本機環境)不一樣?
不過相對於這個問題,我更好奇為什麼VS的printf不支持%n格式,,而gcc卻支持。
英文不好,那個帖子看著有點頭疼。所以想找找中文博客。
果然知道了問題,查詢工作事半功倍,一下就找到了完美解決所有疑惑的博客(連解決方案都有了):
使用printf修改變量的值 —— VS2008中使用%n輸出遇到的問題及解決方法
原來是因為printf函數使用%n可以提供一個變量指針,所以允許printf修改一個變量的值,存在安全隱患,VS就默認禁止了這種用法。而且提供了開啟的方法:定義一個宏控制就行了(其實VS很多地方採用這種方法提供配置,只是自己不知道)。
#define _set_printf_count_output 1
反思一下,從同樣的地方查資料,為什麼自己連根毛都沒找到,人家卻能完美解決問題呢?這是一種能力體現,查閱文獻不是一件非常簡單的事情,也是需要技巧和經驗的積累。
善於發現,善於思考,善於學習。
共勉。
PS. 突然發現s_s這個符號好可愛的有沒有。