不定長度引數


在定義函式時,有時無法事先得知要傳遞的參數個數,透過陣列收集是方式之一,例如:

#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_startva_argva_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