printf/sprintf 编译器警告是概念上的破坏吗?

发布于 2024-09-19 09:40:11 字数 301 浏览 4 评论 0原文

我注意到,当 printf/sprintf 函数的格式字符串中的转换说明符与相应参数的类型或计数不匹配时,大量 C 编译器会发出警告。

在我看来,这似乎是一个概念上的突破,因为根据语言规范,C 没有内置函数。

编译器应该了解 printf/sprintf 的所有内容是它们的原型,而不是它们的语义。我知道 printf/sprintf 是标准 C 函数,但它们驻留在单独的库 libc 中,并且您必须包含 stdio.h 才能导入它们的原型。

相反,许多编译器所做的是分析也可以在运行时提供的格式字符串。

上面说的有道理吗?

I have noticed that a large number of C compilers issue warnings when the conversion specifiers in the format string of the printf/sprintf functions do not match the type or the count of the corresponding arguments.

That seems to me like a conceptual break since C doesn't have built-in functions according to the language specification.

All the compiler should know about printf/sprintf is their prototypes and not their semantics. I know that printf/sprintf are standard C functions, but yet they reside in a separate library, libc, and you have to include stdio.h to import their prototypes.

What many compilers do instead is analyze the format string which could as well be supplied at runtime.

Does the above make sense?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(7

極樂鬼 2024-09-26 09:40:11

“编译器应该了解 printf/sprintf 的只是它们的原型,而不是它们的语义”。

这就是不真实的部分。就标准而言,C 实现的任何部分都“允许”了解任何其他部分,并发出可能对用户有帮助的诊断信息。标准不要求编译器内在函数,这种特定的诊断也不是,但它们当然不会被禁止。

请注意(就标准而言)标准库很特殊,它不仅仅是任何旧的链接库。如果特定的实现/编译器甚至为用户提供了一种链接不同版本的标准库的机制,那么当该替代库具有与标准库中规定的语义不同的语义时,标准当然不要求它“工作”。标准。

所以从这个意义上说,标准库中的所有内容都是“内置的”。它是 C 语言规范的一部分。编译器可以根据其行为符合标准要求的假设进行操作。

当然,如果直到运行时才知道格式说明符,则编译器无法对可变参数进行静态检查。但是,当它在编译时已知时,编译器可以有效地假定 printf 的行为,就像它可以有效地假定 memcpy 或整数加法的行为一样。

"All the compiler should know about printf/sprintf is their prototypes and not their semantics".

That's the part that isn't true. As far as the standard is concerned, any part of a C implementation is "allowed" to know about any other part, and to issue diagnostics that may be helpful to the user. Compiler intrinsics aren't required by the standard, and neither is this particular diagnostic, but they certainly aren't forbidden.

Note that (as far as the standard is concerned) the standard library is special, it's not just any old linked library. If a particular implementation/compiler even provides a mechanism for the user to link against a different version of the standard library, the standard certainly doesn't require it to "work" when that alternative library has different semantics from what is laid out in the standard.

So in that sense, everything in the standard library is "bult-ins". It's part of the C language specification. Compilers are allowed to act on the assumption that it behaves as the standard requires.

Of course, if the format specifier isn't known until runtime, then the compiler can't do a static check of the varargs. But when it is known at compile time, the compiler can assume the behaviour of printf just as validly as it can assume the behaviour of memcpy, or of integer addition.

梦纸 2024-09-26 09:40:11

如果我正确地阅读了您的问题,我同意您的前提,即编译器对 printf 和朋友的格式字符串的验证在概念上不同于其他类型的静态检查(语法、类型等)。由编译器完成。

然而,它是标准允许的,并且极大地帮助了我们贫穷的程序员。

If I read your question correctly, I agree with your premise that verification of printf and friends' format strings by the compiler is an activity conceptually unlike the other sorts of static checking (syntax, type, etc.) done by the compiler.

However, it is permitted by the standard, and helps us poor programmers out greatly.

熊抱啵儿 2024-09-26 09:40:11

编译器在这里的任务只是给你一些有用的提示。此行为不属于标准范围。

实现可能会在许多情况下生成警告,但其中没有一个是
指定为本国际标准的一部分。

从理论上讲,没有什么可以阻止编译器警告您有关(可能)错误使用(例如 QT 库)的信息。

printf 是标准函数,因为它(包括其语义)被 ISO C 标准涵盖。

Compiler's task here is just to give you some useful hints. This behaviour is not covered by standard.

An implementation may generate warnings in many situations, none of which are
specified as part of this International Standard.

In theory, nothing prevents compiler from warning you about (potentially) incorrect usage of, say, QT library.

And printf is standard function in the sense that it (including its semantics) is covered by ISO C standard.

谜兔 2024-09-26 09:40:11

该标准要求在某些情况下进行诊断,但并不禁止一般诊断。任何实现都可以出于任何原因自由发出诊断,包括 printf() 的不当使用或字母 Q 的过度使用。显然,其中一些原因比其他原因更有用。

此外,如果您包含一个库,则其中的所有可见标识符都将被保留。您不允许 #include 并拥有自己的 printf 定义(请参阅 C99 标准草案的 7.1.3)。这意味着该实现可以自由地假设您正在使用标准 printf 并将其视为标准的必需部分。

The Standard requires diagnostics under some circumstances, but does not disallow general diagnostics. Any implementation is free to issue a diagnostic for any reason, including improper use of printf() or the overuse of the letter Q. Obviously, some of these reasons are more useful than others.

Moreover, if you include a library, all the visible identifiers in it become reserved. You are not allowed to #include <stdio.h> and have your own definition of printf (see 7.1.3 of the draft C99 standard). This means that the implementation is free to assume that you're using the standard printf and treat it as if it were a required part of the standard.

不乱于心 2024-09-26 09:40:11

此类警告表明可能存在错误,因此非常有用。

是的,编译器中出现警告的特殊情况可能看起来不一致(假设 不只是有 __printf_format_warning 属性或类似的东西) ,但话又说回来,如果它有用并且有助于解决一些错误(甚至可能是安全错误),那么为什么不拥有它们呢?

我的意思是,并不是每个人都用自己的 libc 替换他们自己的,具有不同的 printf 语义......

These kind of warnings indicate likely bugs and, as a result, are useful.

Yes, it might look inconsistent to have special cases for warnings in the compiler(assuming <stdio.h> doesn't just have a __printf_format_warning attribute or something like that), but then again, if it useful and helps solve some bugs(maybe even security bugs), then why not have them?

I mean, it's not like everyone just replaces their libc with their own, with different printf semantics...

天邊彩虹 2024-09-26 09:40:11

编程语言标准的最终目的是帮助程序员编写按预期运行的程序。标准中没有任何内容表明编译器在遇到“bigvar = byte3 << 24 + byte2 << 16 + byte1 << 8 + byte0;”时应发出警告,但由于结果可能不是程序员想要的,许多编译器会发出警告。标准对警告施加的唯一限制是,它们不能阻止合法程序的成功编译(例如,编译器在输出 999 个警告后因错误而失败,或者输出如此多的警告,以至于编译器出于所有实际目的,永远不完整,将是不合格的)。

没有任何要求编译器“了解”标准库,但也没有要求编译器不了解它们(如果有人 #includes 普通标头)。事实上,如果程序包含;我认为根据标准,编译器可以用运行时更容易处理的内容替换它可以理解的 printf 调用(例如,它可以替换 printf("Q%5d",foo); 与“putch('Q'); __put_int(foo,5);”(如果需要)。如果程序没有 #include这种翻译将被禁止。

The ultimate purpose of programming language standards is to help programmers write programs that behave as intended. There is nothing in the standard that says a compiler should issue a warning if it encounters "bigvar = byte3 << 24 + byte2 << 16 + byte1 << 8 + byte0;", but since the results probably aren't what the programmer intended, many compilers will issue a warning. The only limitation the standards impose upon warnings is that they must not prevent the successful compilation of a legitimate program (e.g. a compiler which failed with an error after outputting 999 warnings, or which output so many warnings that compilation would, for all practical purposes, never complete, would be non-conforming).

There isn't any requirement that the compiler "know" about the standard libraries, but nor is there any requirement that it not know about them if someone #includes the normal headers. Indeed, if a program includes <stdio.h> I think it would be permissible under the standard for a compiler to replace a printf call that it can understand with something that might be easier to process at run-time (e.g. it could replace printf("Q%5d",foo); with "putch('Q'); __put_int(foo,5);" if desired). If a program does not #include <stdio.h> such translation would be forbidden.

雨后彩虹 2024-09-26 09:40:11

这是概念上的突破吗?是的。但“概念上的突破”是,无论出于何种意图和目的,C 现在确实具有“内置函数”。它们在标准中指定,它们的名称都是保留的(除非您处于独立环境中),并且编译器以各种方式对它们进行特殊处理。

例如,如果我在没有正确包含 的情况下调用 printf,我通常会收到类似“内置函数 'printf 的不兼容隐式声明”之类的警告'” (gcc),或“隐式声明类型为 'int (const char *, ...)' 的库函数 'printf'” (clang)。这两条消息都证明编译器一直都知道 printf,无论我是否显式包含一个带有外部声明的头文件来告诉编译器一些信息。

鉴于编译器确实了解库函数,因此在类 printf 函数的情况下,他们利用这些知识所做的事情之一是仔细检查实际参数,这是完全合适的。

是的,我确实记得库函数确实没有以任何方式内置的日子,而且我怀念那些日子的一些事情,但实际上,编译器现在确实知道库函数不会给我带来任何问题,也不会影响我的睡眠。

我坚信,对于现代编译器来说,检查传递给 printflike 函数的参数的数量和类型或多或少是强制性的。回到过去的美好时光,当库函数还不是语言的一部分时,函数原型也不存在。如果程序员想要确保函数调用与其定义相匹配,则程序员要么要小心,要么运行 lint。但原型改变了这一点,今天的程序员知道调用 sqrt(144) 是可以的。但出于同样的原因,今天的程序员不明白为什么他们不能调用printf("%f\n", 144)。我真的不能为此责怪今天的程序员。

Is it a conceptual break? Yes. But the "conceptual break" is that for all intents and purposes, C does now have "built-in functions". They're specified in the Standard, their names are all reserved (unless you're in a freestanding environment), and compilers are special-casing them in all sorts of ways.

For example, if I call printf without properly including <stdio.h>, I'll typically get a warning like "incompatible implicit declaration of built-in function ‘printf’" (gcc), or "implicitly declaring library function 'printf' with type 'int (const char *, ...)'" (clang). Both messages prove that the compiler knew about printf all along, whether or not I explicitly included a header file with an external declaration that told the compiler something.

Given that compilers do know about library functions, it's perfectly appropriate that one of the things they do with this knowledge, in the case of printflike functions, is to double-check the actual arguments.

Yes, I do remember the days when library functions truly weren't built in, in any way, and there were things about those days that I miss, but really, the fact that compilers do now know about library functions isn't causing me any problems or costing me any sleep.

And I firmly believe that for a modern compiler to check the number and types of arguments handed to printflike functions is more or less mandatory. Back in the good old days, back when library functions weren't part of the language, it was also the case that function prototypes didn't exist. If a programmer wanted to ensure that a function call matched its definition, it was up to the programmer either to be careful, or to run lint. But prototypes changed that, and today's programmers know that it's fine to call, say, sqrt(144). But for the same reason, today's programmers don't understand why they can't call printf("%f\n", 144). And I really can't blame today's programmers for that.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文