简体   繁体   English

在没有适当原型的情况下调用 printf 是否会调用未定义的行为?

[英]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 调用可能需要传递pq ,和x作为三个 arguments,将pqx的地址作为三个 arguments 传递,或者构建一个包含qx的结构,并将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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM