跳到主要內容

關於Visual Studio中printf(printf_s)函數用%n輸出值的問題

· 閱讀需 4 分鍾

今天遇到件有趣的事。看到有人問以下代碼為什麼出錯:

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這個符號好可愛的有沒有。