问题 C编译器如何使用可变数量的参数实现函数?


我几天前参加了技术面试,有人问我 C编译器的implments如何使用可变数量的参数? 它是如何通过堆栈的?

有人知道或可以探索吗?

谢谢, 担


9499
2018-04-29 17:40


起源

据我所知,varargs函数依赖于实现。 - WhirlWind


答案:


据我所知,用C ......

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

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


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


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


10
2018-04-29 17:44



这很清楚,但你忽略了被调用者的角色。被调用者(例如printf)必须使用一个或多个强制参数(如格式字符串)来知道堆栈上的参数应该是什么。然后它使用目标特定的宏来访问那些作为数组(系统堆栈是一个可以移动头部的数组)。 stdarg.h头文件包含你需要执行此操作的宏,并且通常通常将指向此数组头部的指针传递给期望处理该函数的函数(如vprintf)。 - nategoose
@nategoose:谢谢你的宝贵补充。只是为了让你知道,我最初故意把它留在了外面,因为OP问怎么了 编译器 实现变量参数列表,而不是如何 程序员 实际上利用它们。我现在添加了一个参考文档的超链接。 - stakx


它使用va_宏实现它们 - 例如 的va_start。确切地说,这些宏所做的是实现定义 - 换句话说,它将从CPU架构到架构,从编译器到编译器不等。但是他们必须在C调用堆栈中玩弄技巧。通常,这将涉及将最后一个命名参数的地址作为基础,然后通过在此基础上执行指针算法来访问可变参数。


7
2018-04-29 17:44



是的,我记得这就是为什么你总是需要一个命名参数... - dicroce


到目前为止,你很难过在技术面试中得到这个问题,我会假设正确答案是:

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

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


0
2018-04-29 17:54





查看va_start,va_arg和va_end。 这里 这是一个很大的信息。


-1
2018-04-29 17:46