C语言中printf函数的定义是:
int printf(const char * _Format, ...);
Run Code Online (Sandbox Code Playgroud)
对于scanf管理可变数量的参数的许多类似函数也是如此.
为什么有_Format强制参数?
格式字符串是必需的,因为C的可变参数宏的工作方式取决于至少存在一个参数,并使用它来查找其他参数.
具体来说,要读取其他变量参数,可以使用va_start(然后va_arg重复一次,对于要读取的每个变量参数).当你调用时va_start,你需要传递格式字符串(或者更常见的是,函数的最后一个非变化参数).
例如,这就像printf,但打印到两个stdout和您选择的另一个文件:
void tee(FILE *f, char const *fmt, ...) {
va_list ap;
va_start(ap, fmt);
vprintf(fmt, ap);
va_end(ap);
va_start(ap, fmt);
vfprintf(f, fmt, ap);
va_end(ap);
}
Run Code Online (Sandbox Code Playgroud)
这使用vprintf和vfprintf,因此它不(直接)使用va_arg本身,而只va_start和va_end,但是这足以说明该如何fmt参与使用va_start.
有一段时间,这实际上并不需要.当C闪亮而且新的时候,你可以拥有一个相当于的功能:int f(...);.
在第一个C标准化工作,然而,这被淘汰赞成宏上面提到的(va_start,va_arg,va_end)这至少需要一个命名的参数.较旧的宏对调用约定提出了许多要求:
使用传统的C调用约定(所有参数都在栈上传递,参数从右向左推送)这是真的.你基本上只是查看堆栈的顶部,向后移动到返回地址,并且有第一个参数.
对于其他调用约定,事情并非如此简单.例如,只需从左向右推送参数意味着第一个参数(格式字符串,在这种情况下printf)被埋在堆栈中的任意距离,其后有任意数量的其他参数.
他们提出处理这个问题的方法是将前一个(命名)参数传递给va_start(并且va_start是一个通常使用该参数地址的宏).如果你从右向左推,那将为你提供一个地址,无论距离堆栈需要什么距离,然后va_arg可以向上走回堆栈以检索其他变量参数.
这显然被视为可接受的折衷方案,特别是因为采用可变参数的函数几乎总是至少采用一个命名参数.
| 归档时间: |
|
| 查看次数: |
335 次 |
| 最近记录: |