关于Visual Studio中printf(printf_s)函数用%n输出值的问题

mattuy 2018年01月17日 208次浏览

今天遇到件有趣的事。看到有人问以下代码为什么出错:

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环境下VC6.0,结果也能正常运行,原因何在?或许VC6.0采用的标准和VS2015(本机环境)不一样?

不过相对于这个问题,我更好奇为什么VS的printf不支持%n格式,,而gcc却支持。

英文不好,那个帖子看着有点头疼。所以想找找中文博客。

果然知道了问题,查询工作事半功倍,一下就找到了完美解决所有疑惑的博客(连解决方案都有了):

使用printf修改变量的值 —— VS2008中使用%n输出遇到的问题及解决方法

原来是因为printf函数使用%n可以提供一个变量指针,所以允许printf修改一个变量的值,存在安全隐患,VS就默认禁止了这种用法。而且提供了开启的方法:定义一个宏控制就行了(其实VS很多地方采用这种方法提供配置,只是自己不知道)。

#define _set_printf_count_output 1

反思一下,从同样的地方查资料,为什么自己连根毛都没找到,人家却能完美解决问题呢?这是一种能力体现,查阅文献不是一件非常简单的事情,也是需要技巧和经验的积累。

善于发现,善于思考,善于学习。

共勉。

PS. 突然发现s_s这个符号好可爱的有没有。