printf 等函数可接受数量不定的实参,称为变参函数(variadic function)。实现变参函数需使用 stdarg.h 中的 va_list、va_start、va_arg、va_end。变参的格式解析与格式说明符密切相关。

变参函数声明

除至少一个固定参数外,使用省略号 ... 表示后续还可传入更多参数:

return_type name(fixed_arg, ...);

stdarg.h 中的四个步骤

1. va_list
声明参数列表遍历用的变量。
va_list list;
2. va_start()
初始化列表;第二个实参为最后一个固定形参名,用于定位变参起始位置。
va_start(list, fixed_arg);
3. va_arg()
按指定类型取下一个变参;类型须与实参一致。
va_arg(list, type);
4. va_end()
结束对列表的使用。
va_end(list);

示例:打印若干个整数

第一个固定参数 n 表示后续整数的个数:

#include <stdio.h>
#include <stdarg.h>

void print(int n, ...) {
    va_list args;
    va_start(args, n);

    for (int i = 0; i < n; i++)
        printf("%d ", va_arg(args, int));
    printf("\n");

    va_end(args);
}

int main(void) {
    print(3, 1, 2, 3);
    print(5, 1, 2, 3, 4, 5);
    return 0;
}

输出:

1 2 3
1 2 3 4 5

变参函数无法自行得知变参个数,须由调用方通过计数参数、哨兵值或格式串等方式约定,printf 即通过格式串中的说明符确定类型与数量。

示例:求和

#include <stdarg.h>
#include <stdio.h>

int getSum(int n, ...) {
    int sum = 0;
    va_list list;

    va_start(list, n);
    for (int i = 0; i < n; i++)
        sum += va_arg(list, int);
    va_end(list);

    return sum;
}

int main(void) {
    printf("1 + 2 = %d\n", getSum(2, 1, 2));
    printf("3 + 4 + 5 = %d\n", getSum(3, 3, 4, 5));
    printf("6 + 7 + 8 + 9 = %d\n", getSum(4, 6, 7, 8, 9));
    return 0;
}

示例:混合类型

#include <stdio.h>
#include <stdarg.h>

void print(int n, ...) {
    va_list args;
    va_start(args, n);

    for (int i = 0; i < n; i++) {
        if (i % 2 == 0) {
            printf("Integer: %d\n", va_arg(args, int));
        } else {
            printf("Float: %.2f\n", va_arg(args, double));
        }
    }
    va_end(args);
}

int main(void) {
    print(4, 10, 3.14, 20, 2.71);
    return 0;
}

注意事项

  • va_arg 的类型须与传入实参一致;float 在变参中按默认实参提升为 double,应以 double 读取。
  • 读取次数不得超过实际传入的变参个数,否则为未定义行为。
  • 每个 va_start 应配对一次 va_end。

本节介绍了变参函数的声明与 stdarg.h 用法。下一节讨论在错误路径上使用goto 集中清理资源的写法。