printf如何发出编译器警告?

11

我在想如何让一个函数发出编译时警告?

这是因为当我们在printf(scanf)的第一个参数中使用错误的格式说明符与匹配该类型说明符的变量并使用gcc的-Wall选项进行编译时,编译器会发出警告。

现在,据我所知,printf和scanf通常作为变量参数函数来实现,我不知道有什么方法可以在编译时检查字符串的值,更不用说如果某些内容不匹配就发出警告了。

有人能解释一下如何得到编译器警告吗?


编译器不知道你给它什么格式字符串(因此可以知道格式说明符),以及你传递给 printf 的数据类型,即使有可变数量的参数?所以它可以轻松地判断您是否存在不匹配的数据和说明符。 - lurker
1
是的,这样做也可以使用它看到的任何其他函数。Printf只是另一个stdio.h函数,与您的或我的任何函数没有区别。 - user1635881
1
嗯,我在想这是否可能 :) - user1635881
1
啊,我明白了。好问题!(+1) - lurker
1
@user1635881,“printf是另一个stdio.h函数,不应与任何我的或你的函数有任何区别。” - 并非如此,“printf()”在C标准中指定,因此它语言的一部分。 - user2371524
显示剩余2条评论
3个回答

12

警告是与实现(即编译器和C标准库)相关的。你可能会得到很少的警告(看看tinycc...),甚至没有警告...

我正在关注最近的GCC(例如4.910...)在Linux上。

您会收到这样的警告,因为printf使用适当的__attribute__进行声明(请参阅GCC function attributes

(使用GCC,您也可以使用format属性声明自己的类似于printf的函数...)

顺便说一句,符合标准的编译器可以非常特别地实现<stdio.h>头文件。因此,它可以通过更改内部状态而不读取任何头文件文件来处理#include <stdio.h>

您甚至可以添加自己的函数属性,例如通过使用您的GCC插件自定义您的GCC。


编译器仍然对类似printf的函数进行特殊处理,因为它在编译时检查格式字符串。我可以看到有警告属性,但它仍然是在编译时生成的(当带有该属性的函数在死代码消除后被调用时)。 - user1635881
3
@user1635881 是的——而考虑到这一点,这确实是一个明智的做法:printf() 不仅是标准的一部分,它还有着众所周知的格式字符串契约(甚至在很多自定义函数中使用),可以经受得住一些静态诊断,那么为什么不把这种调试辅助功能提供给程序员呢? - user2371524
我同意,但这可能会打开潘多拉魔盒。你看,编译器可能会为其他函数添加静态分析,你可能会习惯于此,但当你转向另一个编译器时,该功能可能会丢失。因此,也许最好将其标准化处理? - user1635881
不可以,因为警告甚至不被标准所知。这是一个实现特定的事情。 - Basile Starynkevitch

6

printf如何触发编译器警告?

一些编译器在编译时分析printf()scanf()的格式和其他参数类型。

printf("%ld", 123);  // type mis-match  `long` vs. `int`
int x;
printf("%ld", &x);  // type mis-match 'long *` vs. `int *`

如果格式已计算,则不会发生该检查,因为这是一个运行时问题。

const char *format = foo();
printf(format, 123);  // mis-match? unknowable.

0

你说得没错,编译器警告特定函数确实很不寻常。

printf(以及scanf和相关函数)格式说明符的警告非常罕见,但是这些函数本身就非常罕见。

正如其他答案所解释的那样,编译器至少可以“知道”某些函数并执行特殊的、额外的编译时检查,因为printfscanf和它们的伙伴同时非常罕见又非常流行,所以编译器进行这种额外检查是相当合适的,尽管它很不寻常。

从前(我在谈论 ANSI 之前的 K&R 时代),C 程序员知道他们必须小心地调用带有正确数量和类型参数的函数。(在那个时代,唯一自动检查的方法是使用lint,一些程序员使用了它,但许多程序员没有。)如果你习惯于小心谨慎,对于printf和它的伙伴也很容易小心谨慎。

今天,情况有所不同。 ANSI C函数原型已经使用了一代。大多数程序员今天都默认编译器会自动转换函数参数的类型,并抱怨不兼容的匹配。 (举个例子,事情发生了变化:在旧时代,调用sqrt(144)是一个错误,会悄悄地给出神秘的结果,但今天没问题。)
所以今天,我非常同情那些正在学习C并被printf困惑的程序员。如果您完全习惯于函数原型提供的保护措施,那么为什么会出现这种神秘的情况,还是一个相当大的谜。
int i = 3;
float f = 4.5;
printf("i as a float is %f, f as an int is %d\n", i, f);

不起作用。与过去不同,我怀疑现在很难记住,当您调用printf(但几乎只有在调用printf时),它是您的工作来正确获取所有类型,因为编译器不会插入任何隐式转换。

底线是,今天,编译器不仅可以警告调用printf等函数时的不匹配,我认为这几乎是一种道义上的必要。当我们引入函数原型时,我们承诺程序员函数参数的类型安全性,因此在涉及printf时悄悄撤回该承诺真的不公平。

[附言:是的,我当然知道为什么函数原型不能保证像printf这样的可变参数函数的完全类型安全。但这与我的论点无关。另外,是的,我知道,生活并不公平,所以你可以称呼我为一个高谈阔论的老软蛋。 :-) ]


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接