[英]Does calling printf without a proper prototype invoke undefined behavior?
Does this innocent looking program invoke undefined behavior:这个看起来无辜的程序是否会调用未定义的行为:
int main(void) {
printf("%d\n", 1);
return 0;
}
Yes invoking printf()
without a proper prototype (from the standard header <stdio.h>
or from a properly written declaration) invokes undefined behavior. 是调用printf()
而没有适当的原型(来自标准头<stdio.h>
或来自正确编写的声明)调用未定义的行为。
As documented in C11 Annex J (informative only) 如C11附录J中所述(仅供参考)
J2 Undefined Behavior J2未定义的行为
- For call to a function without a function prototype in scope where the function is defined with a function prototype, either the prototype ends with an ellipsis or the types of the arguments after promotion are not compatible with the types of the parameters (6.5.2.2). 对于在函数原型定义函数的范围内调用没有函数原型的函数,原型以省略号结束或者升级后的参数类型与参数类型不兼容(6.5.2.2) 。
This annex is not normative, but clearly documents the above code as an example of undefined behavior. 本附件不是规范性的,但清楚地将上述代码记录为未定义行为的示例。
In more pragmatic words, in the absence of a prototype for printf
, the compiler generates the calling sequence as if printf
was defined as int printf(const char*, int)
which may be quite different and incompatible with the actual implementation of printf
in the standard library, defined as int printf(const char restrict *format, ...)
. 用更实用的词来说,在没有printf
原型的情况下,编译器会生成调用序列,就好像printf
被定义为int printf(const char*, int)
,这可能是完全不同的,并且与printf
的实际实现不兼容。标准库,定义为int printf(const char restrict *format, ...)
。
Ancient ABIs were regular enough that this would not cause a problem, but modern (eg 64-bit) ABIs use more efficient calling sequences that make the above code definitely incorrect. 古代ABI足够常规,这不会引起问题,但现代(例如64位)ABI使用更有效的调用序列,使上述代码绝对不正确。
As a consequence, this famous classic C program would fail too, without the #include <stdio.h>
or at least a proper prototype for printf
: 因此,这个着名的经典C程序也会失败,没有#include <stdio.h>
或者至少是printf
原型:
int main(void) {
printf("Hello world\n"); // undefined behavior
return 0;
}
C was originally implemented on platforms where passing a variable number of arguments to a function call wouldn't pose any difficulty, and where a function call like foo("Hey", "there", 123);
C 最初是在平台上实现的,在这些平台上,将变量 arguments 传递给 function 调用不会造成任何困难,而 function 调用如foo("Hey", "there", 123);
would be processed the same way if the function signature were any of the following:如果 function 签名是以下任何一种,将以相同的方式处理:
int foo(char const *p, char const *q, int x);
int foo(char const *p, char const *q, ...);
int foo(char const *p, ...);
On some platforms, however, the number and format of passed arguments must be known in advance;但是,在某些平台上,必须事先知道传递的 arguments 的数量和格式; C implementations for such platforms may accommodate this by treating ...
as a void*
, and having calling code construct and pass the argument of a structure containing the passed arguments. On such platforms, the function call may need to pass p
, q
, and x
as three arguments, pass p
, q
, and the address of x
as three arguments, or build a structure holding q
and x
, and pass p
along with the address of that structure as two arguments.此类平台的 C 实现可以通过将...
视为void*
来适应这一点,并让调用代码构造并传递包含传递的 arguments 的结构的参数。在此类平台上, function 调用可能需要传递p
, q
,和x
作为三个 arguments,将p
、 q
和x
的地址作为三个 arguments 传递,或者构建一个包含q
和x
的结构,并将p
和该结构的地址作为两个 arguments 传递。
If a compiler doesn't know where ...
belongs in an argument list, it would have no way of knowing how to format the arguments.如果编译器不知道...
在参数列表中的位置,它就无法知道如何格式化 arguments。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.