C 编译器如何实现具有可变数量参数的函数?

Posted

技术标签:

【中文标题】C 编译器如何实现具有可变数量参数的函数?【英文标题】:How does the C compiler implement functions with Variable numbers of arguments? 【发布时间】:2011-02-13 22:23:57 【问题描述】:

前几天我参加了一个技术面试,被问到 它是如何通过栈上的?

有谁知道或者可以解释一下吗?

谢谢, 丹

【问题讨论】:

据我所知,可变参数函数是依赖于实现的。 【参考方案1】:

据我所知,用 C...

调用函数按从右到左的顺序将参数压入堆栈。

调用者负责在被调用函数执行后从堆栈中删除参数。这可能正是因为调用者被保证知道它在堆栈上放了多少参数,而被调用的函数可能会出错。


P.S.: 调用约定通常是特定于实现的。我刚刚描述的称为“cdecl”调用约定。将此与通常称为“stdcall”的调用约定进行对比,其中被调用函数负责从堆栈中删除其参数。因此,它不支持变长参数列表。


P.P.S.:正如用户 nategoose 评论的那样,我没有提到变量参数列表实际上是如何使用的。参见例如POSIX documentation for the <stdarg.h> header 了解更多信息。

【讨论】:

这很清楚,但是你忽略了被调用者的角色。被调用者(如 printf)必须使用一个或多个强制参数(如格式字符串)才能知道堆栈上的参数应该是什么。然后它使用特定于目标的宏以数组的形式访问它们(系统堆栈是一个可以移动其头部的数组)。 stdarg.h 标头包含您需要执行此操作的宏,并且通常通常将指向该数组头部的指针传递给期望对其进行处理的函数(例如 vprintf)。【参考方案2】:

它使用 va_ 宏来实现它们 - 例如va_start。这些宏的确切作用是实现定义的——换句话说,它会因 CPU 架构和编译器而异。但是他们必须在 C 调用堆栈上耍花招。通常,这将涉及将最后一个命名参数的地址作为基址,然后通过在此基址上执行指针运算来访问可变参数。

【讨论】:

【参考方案3】:

就你在技术面试中被问到这个问题感到难过,我认为正确的答案是:

调用者将显式参数推送到堆栈、可变参数的计数和可变参数本身。然后目标函数代码将负责根据传递的计数和堆栈地址弹出所有参数。

并补充一些想法,为什么将这个参数放在单独的数组中不方便。

【讨论】:

【参考方案4】:

查看 va_start、va_arg 和 va_end。 Here 是关于这方面的大量信息。

【讨论】:

以上是关于C 编译器如何实现具有可变数量参数的函数?的主要内容,如果未能解决你的问题,请参考以下文章

C++11:可变参数模板函数参数的数量?

如何使 Clojure 函数采用可变数量的参数?

可以重用线程来运行可变参数函数吗?

Python 3 类型,具有任意数量的包含类型的自定义可变参数泛型,如何?

在 Postgresql 中创建一个具有多列作为参数的函数

如何使用可变参数模板参数保存可变数量的参数?