Namespaces
Variants

Variadic arguments

From cppreference.net

可变参数函数是指可以接受不同数量参数调用的函数。

只有原型化的 函数声明 可以是可变参数的。这通过形如 ... 的参数表示,该参数必须出现在参数列表的最后位置 且必须跟随至少一个命名参数 (C23前) 。省略号参数与其前面的参数必须使用 , 进行分隔。

// 原型声明
int printx(const char* fmt, ...); // 以此方式声明的函数
printx("hello world");     // 可使用一个
printx("a=%d b=%d", a, b); // 或多个参数调用
int printz(...); // 自 C23 起及在 C++ 中有效
// C23 前错误:... 必须至少跟随一个命名参数
// int printy(..., const char* fmt); // 错误:... 必须是最后一个参数
// int printa(const char* fmt...);   // C 语言错误:要求有 ',';C++ 中有效

函数调用 时,属于可变参数列表的每个实参都会经历特殊的隐式转换,称为 默认实参提升

在使用了可变参数的函数体内,可以通过 <stdarg.h> 库工具 来访问这些参数的值:

定义于头文件 <stdarg.h>
启用可变参数函数的参数访问
(函数宏)
访问下一个可变参数函数的参数
(函数宏)
(C99)
创建可变参数函数参数的副本
(函数宏)
结束可变参数函数的参数遍历
(函数宏)
持有 va_start va_arg va_end va_copy 所需的信息
(类型定义)

目录

注释

尽管旧式(无原型的) 函数声明 允许后续函数调用使用任意数量的参数,但它们不允许是可变参数的(自C89起)。此类函数的定义必须指定固定数量的参数,且不能使用 stdarg.h 宏。

// 旧式声明,已在 C23 中移除
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...

参考文献

  • C17 标准 (ISO/IEC 9899:2018):
  • 6.7.6.3/9 函数声明符(包含原型)(页码: 96)
  • 7.16 可变参数 <stdarg.h> (页码: 197-199)
  • C11 标准 (ISO/IEC 9899:2011):
  • 6.7.6.3/9 函数声明符(包含原型)(页码: 133)
  • 7.16 可变参数 <stdarg.h> (页码: 269-272)
  • C99标准(ISO/IEC 9899:1999):
  • 6.7.5.3/9 函数声明符(包含原型)(第119页)
  • 7.15 可变参数 <stdarg.h>(第249-252页)
  • C89/C90 标准 (ISO/IEC 9899:1990):
  • 3.5.4.3/5 函数声明符(包含原型)
  • 4.8 可变参数 <stdarg.h>

参见

C++ 文档 关于 可变参数