关于可变数量的论点,我有几个问题:
为什么将va_start
,va_arg
和va_end
定义为宏而不是函数?
va_start
如何运作?它是否可以访问函数调用堆栈并遍历堆栈,直到找到最后指定的参数?
答案 0 :(得分:2)
7.15
变量参数部分Rationale for International Standard—Programming Languages—C中介绍了它们为什么是宏的基本原理:
va_start和va_arg必须以宏的形式存在,因为va_start使用的参数是 通过名称传递,va_arg使用一个参数,该参数是数据类型的名称。
本文How Variable Argument Lists Work in C详细介绍了原因并提供了可能的 x86 实现:
typedef char *va_list;
#define va_start( list, param ) (list = (va_list)(¶m + sizeof( param )))
#define va_arg( list, type ) (*(type *)((list += sizeof( type )) - sizeof( type ))
在 C ++ 中,您有很多其他选择,Variable number of arguments in C++?可能涵盖所有其他选择。
答案 1 :(得分:1)
实际上va_end
不需要实现为宏,我认为也不会实现va_start(您只需要将&
添加到参数中以传递指针对于他们而言。va_end
和va_start
必须被视为宏,因为在所有情况下都不能使用&
,正如评论中所指出的那样。 / p>
va_arg
必须作为宏实现,因为您需要提供一个类型作为参数,如果没有宏,就不能这样做。
va_start
按照你的假设工作:你给它第一个参数,它可以根据参数的大小计算其他参数的位置,因为它们在堆栈上都是连续的。
它只是启动va_list
指向第一个参数的末尾(您传递给va_start
)并在每次使用va_arg
时添加下一个参数的大小。< / p>
答案 2 :(得分:0)
va_start
,va_arg
和va_end
通常必须在可变参数函数的上下文中执行才能完成工作,因此将它们设置为函数会使它们复杂得多,或者(在某些情况下)完全无法实施。
其工作原理的细节留待实施。在典型情况下,可变参数函数的参数将从左向右推,因此第一个参数将最接近堆栈的顶部。为了实现它,va_arg
只需要知道堆栈帧的基本结构,例如返回地址的大小(顶部参数通常就在它旁边)。