可變參數函數允許參數個數在編譯期不確定,printf、scanf 即為典型例子。C 語言透過 <stdarg.h> 提供的巨集實現參數遍歷,本文詳細解析其機制與使用規範。
| 巨集 | 功能 | 參數 |
|---|---|---|
va_list | 參數列表型別,保存遍歷狀態 | 變數宣告 |
va_start(ap, last_fixed) | 初始化,定位到第一個可變參數 | ap: va_list 變數; last_fixed: 最後一個固定參數名 |
va_arg(ap, type) | 取得下一個參數並推進位置 | ap: va_list; type: 期望的參數型別 |
va_end(ap) | 清理,釋放資源 | ap: va_list |
va_copy(dest, src) | 複製 va_list (C99) | 需搭配 va_end 成對使用 |
#include <stdio.h>
#include <stdarg.h>
// 第一個固定參數 args 指定後續參數個數
int get_number_total(int args, ...) {
va_list ap;
va_start(ap, args); // 從 args 之後開始
int total = 0;
for (int i = 0; i < args; ++i) {
total += va_arg(ap, int); // 依序取出 int
}
va_end(ap);
return total;
}
int main() {
printf("Total: %d\n", get_number_total(3, 11, 15, 9)); // 35
printf("Total: %d\n", get_number_total(2, 11, 9)); // 20
printf("Total: %d\n", get_number_total(5, 11, 9, 5, 13, 7)); // 45
return 0;
}
va_start 需要最後一個固定參數作為定位基準va_arg(ap, int) 若實際傳入 double,行為未定義(棧對齊、大小不符)args)printf 的 %d 計數)execl 以 NULL 結尾)float → double, char/short → int。取值時用提升後型別(va_arg(ap, double) 取 float)void my_log(const char* fmt, ...) {
va_list ap;
va_start(ap, fmt);
// 這裡簡單示範,實際需完整解析 fmt
vprintf(fmt, ap); // 直接委給 vprintf
va_end(ap);
}
int main() {
my_log("User: %s, Age: %d, Score: %.2f\n", "Alice", 25, 98.5);
return 0;
}
vprintf/vsnprintf 等 v* 系列函數接受 va_list,是實作自訂格式化函數的標準做法。
C++11 引入變參模板(Variadic Templates),提供型別安全、編譯期展開、支援非 POD 型別,優於 C 風格可變參數。C 語言場景下仍以 stdarg.h 為主。
-Wvarargs 檢查格式字串與參數匹配__builtin_va_arg_pack (GCC/Clang) 進行靜態分析