可变数量的参数

Variable numbers of arguments

本文关键字:参数      更新时间:2023-10-16

我有几个关于变量数量的问题:

  1. 为什么va_startva_argva_end被定义为宏而不是函数?

  2. va_start是如何工作的?它是否有权访问函数调用堆栈,并遍历堆栈直到找到最后一个指定的参数?

它们为什么是宏的基本原理在7.15变量自变量部分的国际标准——编程语言——C的基本原理中有介绍,它说:

va_start和va_arg必须作为宏存在,因为va_start使用的参数是通过name传递,va_arg使用一个参数,该参数是数据类型的名称。

这篇文章如何可变参数列表在C中工作更详细地介绍了为什么,并给出了一个可能的x86实现:

typedef char *va_list;
#define va_start( list, param ) (list = (va_list)(&param + sizeof( param )))
#define va_arg( list, type )    (*(type *)((list += sizeof( type )) - sizeof( type ))

C++中,你有很多其他的替代方案和C++中可变数量的参数?可能涵盖了所有这些。

va_end不需要作为宏来实现,我认为va_start也不需要(您只需要将&添加到参数中就可以向它们传递指针)实际上va_endva_start必须作为宏来实现,因为不能在所有情况下都使用&,正如评论中所指出的那样。

va_arg必须作为宏来实现,因为您需要提供一个类型作为参数,而没有宏是无法做到这一点的。

va_start按照您的假设工作:您给它第一个参数,它可以根据该参数的大小计算其他参数的位置,因为它们在堆栈上都是连续的。

它只需启动指向第一个参数(传递给va_start)末尾的va_list,并在每次使用va_arg时添加下一个参数的大小。

va_startva_argva_end通常必须在变量函数的上下文中执行才能完成它们的工作,因此使它们成为函数会使它们变得更加复杂,或者(在某些情况下)完全不可能实现。

它如何工作的细节留给实现。在典型的情况下,变元函数的参数将从左向右推,因此第一个参数将最接近堆栈的顶部。为了达到这个目的,va_arg只需要知道堆栈帧的基本结构,比如返回地址有多大(顶部参数通常就在它旁边)。