C 语言中函数调用约定的历史



我记得,早期的C(例如K&R(允许在任何函数调用上传递任何东西,所以调用约定必须是参数从右向左推送,调用者在函数返回后清除堆栈。

我在演示文稿中遇到了一个难题,其中解决方案涉及在根本不使用任何头文件的情况下调用printf。 他断言,在 C 中,如果你调用一个尚未声明的函数,那么编译器会隐式地将其参数列表作为它看到你传递的提升参数。

但是,在升级到ANSI C时引入的新原型函数调用使用更有效的调用约定,其中被调用的函数清除堆栈;每次使用都不会重复。

在我的记忆中,这两种形式被赋予了不同的链接器可见名称,并且不兼容,这是在链接时捕获的。 我坚持认为,他的例子是有效的,因为printf故意使用旧形式,使任何内容都可以逐个调用传递。

他说,这两种用途必须是兼容的,这是标准规定的。 除非编译器始终生成旧式调用,否则我不明白它是如何工作的。

根据标准的真实情况如何? 而且,它的历史是什么——它是否随着时间的推移而改变?

C 标准没有提到调用约定。

从 1989 年 ANSI C 标准(相当于 1990 年 ISO C 标准(开始,在没有正确声明的作用域的情况下调用像printf这样的可变参数函数具有未定义的行为。该声明必须是原型,并且必须包含, ...序列,以指示接受可变数量和类型的参数。

从 1999 ISO C 标准开始,调用没有可见声明的函数是违反约束的行为,需要进行诊断。(这和C说构造是非法的差不多。在 C99 之前,调用的函数将使用返回类型int隐式声明,并且调用中出现任何(提升的(参数。

许多 C 编译器将接受(可能带有警告(没有声明的调用,并且许多编译器可能使用调用约定,该约定调用没有可见声明"work"的printf。但是该语言没有定义这种调用的行为,并且符合要求的编译器可以自由地拒绝它或生成任意行为异常的代码。

如果要调用printf,只需在源文件的顶部添加#include <stdio.h>即可。这比考虑给定编译器可以逃脱什么要容易得多。

最新更新