不定长度实参


在定义函数时,有时无法事先得知要传递的参数个数,透过数组收集是方式之一,例如:

#include <stdio.h>

void foo(int len, double* params);

int main(void) {
    double x = 1.1, y = 2.1, z = 3.9;
    double a = 0.1, b = 0.2, c = 0.3;

    puts("三个参数:");
    foo(3, (double[]) {x, y, z});

    puts("六个参数:");
    foo(6, (double[]) {x, y, z, a, b, c});

    return 0;
}

void foo(int len, double* params) {
    for(int j = 0; j < len; j++) {
        printf("%.1f\n", params[j]);
    }
}

或许你会觉得double[]形态的指定与{}很烦,这边介绍不定长度实参(Variable-length argument)的使用,为了要使用不定长度实参,必须包含 stdarg.h 标头文件:

#include <stdarg.h>

不定长度实参使用几个识别字来创建不定长度实参:

  • va_list 一个特殊的类型(type),在 va_start、 va_arg 与 va_end 三个宏(macro)中当作参数使用。
  • va_start 启始不定长度实参的宏,第一个实参是 va_list,第二个实参是最后一个具名参数。
  • va_arg 读取不定长度实参的宏。
  • va_end 终止不定长度实参的宏。

在定义不定长度实参时,函数定义时...前至少要有一个具名参数,之后使用...表示将使用不定长度实参,例如:

void foo(int, ...);

在使用va_arg宏取出实参内容时,必须指定将以何种数据类型取出,例如:

va_arg(num_list, double);

下面这个程序示范如何使用不定长度实参:

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

void foo(int, ...);

int main(void) {
    double x = 1.1, y = 2.1, z = 3.9;
    double a = 0.1, b = 0.2, c = 0.3;

    puts("三个参数:");
    foo(3, x, y, z);

    puts("六个参数:");
    foo(6, x, y, z, a, b, c);

    return 0;
}

void foo(int len, ...) {
    va_list args;
    va_start(args, len);

    for(int j = 0; j < len; j++) {
        printf("%.1f\n", va_arg(args, double));
    }

    va_end(args);
}

上例中由于首个参数用来规范不定长度类型,也是唯一的具名参数,就用来作为指定将有几个不定长度实参。执行结果如下:

三个参数:
1.1
2.1
3.9
六个参数:
1.1
2.1
3.9
0.1
0.2
0.3

va_start第二个实参要指定最后一个具名参数,因此未必得以第一个参数指出实参的数量,例如:

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

void print_positive_ints(int, ...);

int main(void) {

    print_positive_ints(1, 2, 3, 4, 5, -1);

    return 0;
}

void print_positive_ints(int first, ...) {
    va_list args;
    va_start(args, first);

    for(int arg = first; arg > 0; arg = va_arg(args, int)) {
        printf("%d\n", arg);
    }

    va_end(args);
}

执行结果:

1
2
3
4
5




展开阅读全文