我想知道函数如何发出编译时警告?
我想到了这一点,因为当我们在printf(scanf)的第一个参数中为与该类型说明符匹配的变量提供错误的格式说明符并使用带有-Wall选项的gcc进行编译时,编译器会发出警告。
现在,printf和scanf定期实现可变参数函数,据我所知,我不知道在编译时检查字符串值的任何方法,更不用说在某些内容不匹配的情况下发出警告。
有人可以解释一下我如何获得编译器警告吗?
答案 0 :(得分:8)
警告是特定的实现(即编译器和C standard library)。你可以让一个编译器提供很少的警告(查看tinycc ...),甚至没有...
我专注于Linux上最近的GCC(例如4.9或5.2 ...)。
您收到此类警告,因为printf
已使用相应的__attribute__
声明(请参阅GCC function attributes)
(使用GCC您同样可以使用printf
属性声明自己的format
类函数...)
<stdio.h>
标头。因此,它可以在不读取任何标题文件的情况下处理#include <stdio.h>
,但可以更改其内部状态。
你甚至可以添加自己的功能属性,例如通过MELT
自定义您的GCC答案 1 :(得分:4)
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.
答案 2 :(得分:0)
您完全正确,编译器警告特定函数是不寻常的。
关于 printf
(和 scanf
及相关)格式说明符的警告很不寻常——但是,这些函数首先是很不寻常的。
正如其他答案所解释的那样,编译器至少有可能“知道”某些函数并执行像这样的特殊的、额外的、编译时检查——并且考虑到 printf
和 scanf
和朋友既非常不寻常又非常受欢迎,编译器进行这种额外的检查是非常合适的,尽管它是不寻常的。
曾几何时(我在这里谈论的是 ANSI 之前的 K&R 时代),C 程序员知道他们必须小心使用正确数量和类型的参数调用函数。 (在那个年代,自动检查的唯一方法是使用 lint
,有些程序员这样做,但许多程序员没有。)如果你习惯了小心,那么小心 printf
很容易{1}} 和朋友也一样。
不过,今天却是另一番景象。 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
这样的可变参数函数的完整类型安全。但这与我在这里的论点无关。另外,是的,我知道,生活是不公平的,所以用我对“道德要求”的高谈阔论称呼我为老软弱的人。 :-)]