变长参数

< c‎ | language

变参数函数是能以不同数目参数调用的函数。

只有新式(原型)函数声明可以为变长参数的。它必须通过出现在参数列表最后的 ... 形式的参数指定,且至少跟随一个具名参数。

//新式声明
int printx(const char* fmt, ...); // 此方法声明的函数
printx("hello world"); // 可能会以一个
printx("a=%d b=%d", a, b); // 或更多参数调用
 
// int printy(..., const char* fmt); // 错误: ... 必须在最后
// int printz(...); // 错误: ... 必须跟随至少一个具名参数

函数调用中,每个属于变长参数列表一部分的参数会经历名为默认参数提升的隐式转换。

在函数体内使用变长参数时,这些参数的值必须用 <stdarg.h> 库工具访问:

定义于头文件 <stdarg.h>
令函数得以访问可变参数
(宏函数)
访问下一个函数可变参数
(宏函数)
创造函数可变参数的副本
(宏函数)
结束函数可变参数的行程
(宏函数)
保有 va_start 、 va_arg 、 va_end 及 va_copy 所需信息
(typedef)

注意

虽然旧式(无原型)函数声明允许后继的函数调用使用任意参数,它们也不允许是变长参数( C89 起)。这种函数的定义必须指定固定数目的参数,并且不能使用 stdarg.h 中的宏。

//旧式声明
int printx(); // 此方式定义的函数
printx("hello world"); // 可以以一个
printx("a=%d b=%d", a, b); // 或更多参数调用
// 上述调用行为至少有一个是未定义的,取决于函数定义所接收的参数数

示例

#include <stdio.h>
#include <time.h>
#include <stdarg.h>
 
void tlog(const char* fmt,...)
{
    char msg[50];
    strftime(msg, sizeof msg, "%T", localtime(&(time_t){time(NULL)}));
    printf("[%s] ", msg);
    va_list args;
    va_start(args, fmt);
    vprintf(fmt, args);
    va_end(args);
}
 
int main(void)
{
   tlog("logging %d %d %d...\n", 1, 2, 3);
}

输出:

[10:21:38] logging 1 2 3...

引用

  • C11 standard (ISO/IEC 9899:2011):
  • 6.7.6.3/9 Function declarators (including prototypes) (p: 133)
  • 7.16 Variable arguments <stdarg.h> (p: 269-272)
  • C99 standard (ISO/IEC 9899:1999):
  • 6.7.5.3/9 Function declarators (including prototypes) (p: 119)
  • 7.15 Variable arguments <stdarg.h> (p: 249-252)
  • C89/C90 standard (ISO/IEC 9899:1990):
  • 3.5.4.3/5 Function declarators (including prototypes)
  • 4.8 VARIABLE ARGUMENTS <stdarg.h>

参阅