定義於 <stdarg.h>:
| 巨集/型別 | 功能 | 典型用法 |
|---|---|---|
va_list | 參數列表游標型別 | va_list ap; |
va_start(ap, last_fixed) | 初始化游標指向第一個可變參數 | va_start(ap, n); |
va_arg(ap, type) | 取得下一個參數值並推進游標 | int x = va_arg(ap, int); |
va_end(ap) | 清理游標 (某些平台必要) | va_end(ap); |
va_copy(dest, src) | 複製游標 (C99,多次遍歷需用) | va_copy(ap2, ap); |
#include返回型別 函數名(固定參數, ...) { va_list ap; va_start(ap, 最後一個固定參數); // 迴圈取得可變參數 while (條件) { 型別 val = va_arg(ap, 型別); // 處理 val } va_end(ap); return 結果; }
#include#include int Max(int n, ...) { va_list ap; va_start(ap, n); // n 是最後一個固定參數 int max = -2147483648; // INT_MIN for (int i = 0; i < n; i++) { int val = va_arg(ap, int); if (val > max) max = val; } va_end(ap); return max; } int main() { printf("Max(3, 1, 5, 3) = %d\n", Max(3, 1, 5, 3)); // 5 printf("Max(5, -2, 0, 9, 4, 1) = %d\n", Max(5, -2, 0, 9, 4, 1)); // 9 printf("Max(1, 42) = %d\n", Max(1, 42)); // 42 return 0; }
void myPrintf(const char *fmt, ...) {
va_list ap;
va_start(ap, fmt);
for (const char *p = fmt; *p; p++) {
if (*p != '%') {
putchar(*p);
continue;
}
p++; // 跳過 %
switch (*p) {
case 'd': {
int val = va_arg(ap, int);
printf("%d", val);
break;
}
case 's': {
char *val = va_arg(ap, char*);
printf("%s", val);
break;
}
case 'f': {
double val = va_arg(ap, double); // float 自動提升為 double
printf("%f", val);
break;
}
case 'c': {
int val = va_arg(ap, int); // char 提升為 int
putchar(val);
break;
}
default:
putchar('%'); putchar(*p);
}
}
va_end(ap);
}
| 規則 | 說明 | 錯誤範例 |
|---|---|---|
| 至少一個固定參數 | C 標準要求,用於 va_start 定位 | void f(...) 非標準 |
| 型別必須匹配 | va_arg(ap, int) 取 long → 未定義行為 | 傳 float 用 va_arg(ap, float) 錯誤 (會提升為 double) |
| 無法自動得知參數個數 | 需透過固定參數 (如 n、格式字串) 隱含傳遞 | 呼叫端少傳參數 → 讀取垃圾值 |
| 預設引數提升 | float→double, char/short→int | 用 va_arg(ap, float) 永遠錯誤 |
多次遍歷需 va_copy | 游標為單向,二次使用前必須複製 | 第二次 va_start 前未 va_end → 未定義 |
printf/scanf 族函數execl/execv 程式啟動pthread_create 執行緒參數傳遞syslog 系統日誌可變參數列表是 C 語言實現多態函數介面的核心機制。掌握 va_list 四大巨集流程、預設引數提升規則、型別匹配鐵律 與 va_copy 多次遍歷技巧,即能設計出如 printf 般靈活、強大的 API。實務中建議:優先考慮結構體/陣列替代 (型別安全)、必要時才用可變參數、詳細文件化參數協定、加入執行期斷言驗證。