| 宏 | 功能 | 典型用法 |
|---|---|---|
va_list | 定義參數列表遍歷器(不透明類型) | `va_list ap;` |
va_start(ap, last_fixed) | 初始化 `ap` 指向第一個可變參數 | `va_start(ap, n2);` // `n2` 為最後一個固定參數 |
va_arg(ap, type) | 取得當前參數值並將 `ap` 移至下一個 | `int val = va_arg(ap, int);` |
va_end(ap) | 清理 `ap` (釋放資源/重置) | `va_end(ap);` |
va_copy(dest, src) | C99 新增:複製 `va_list` (避免二次遍歷失效) | `va_copy(ap2, ap1);` |
關鍵規則:可變參數**無自我描述能力**,調用者必須透過固定參數(如格式字串、計數器、哨兵值)告知函數參數個數與類型。
#include
#include
// 固定前兩參數,後續可變,遇 0 結束
int sum_sentinel(int n1, int n2, ...) {
va_list ap;
int sum = n1 + n2; // 先累加固定參數
va_start(ap, n2); // 從 n2 之後開始
int val;
while ((val = va_arg(ap, int)) != 0) { // 哨兵值 0
sum += val;
}
va_end(ap);
return sum;
}
int main() {
printf("%d\n", sum_sentinel(2, 2, 2, 0)); // 6
printf("%d\n", sum_sentinel(1,2,3,4,5,6,7,8,9,0)); // 45
return 0;
}
int sum_sentinel_v2(int n1, ...) {
va_list ap;
int sum = n1;
va_start(ap, n1);
int val;
while ((val = va_arg(ap, int)) != 0) sum += val;
va_end(ap);
return sum;
}
// sum_sentinel_v2(2, 0) -> 2
double sum_count(int count, double first, ...) {
va_list ap;
double sum = first;
va_start(ap, first);
for (int i = 1; i < count; i++) { // 已取 first,剩 count-1 個
sum += va_arg(ap, double);
}
va_end(ap);
return sum;
}
// sum_count(3, 1.1, 2.2, 3.3) -> 6.6
// sum_count(2, 1.1, 2.2, 3.3) -> 3.3 (忽略多餘參數)
double sum_format(const char *fmt, ...) {
va_list ap;
double sum = 0.0;
va_start(ap, fmt);
for (; *fmt; fmt++) {
if (*fmt == '%') {
fmt++; // 看下一個字元
switch (*fmt) {
case 'd': sum += va_arg(ap, int); break; // int 提升自動處理
case 'f': sum += va_arg(ap, double); break; // float 自動提升為 double
default: break; // 忽略未知格式
}
}
}
va_end(ap);
return sum;
}
// sum_format("%d,%f,%d,%f", 1, 1.1, 2, 2.2) -> 6.3
可變參數提供極大靈活性,但犧牲類型安全與效能。現代 C 開發中,固定參數 + 結構體/陣列/指標 往往是更佳選擇。若必須使用,嚴格遵循「計數器/格式字串驅動」原則,並善用 `va_copy` 處理複雜邏輯。