C 語言通過 <stdarg.h>(C89)實現可變參數函數。本文詳解 va_list、va_start、va_arg、va_end 四大宏機制,並實現求平均值、求最大值、模擬 printf 三個進階示例。
void func(int fixed, ...) {
va_list ap;
va_start(ap, fixed); // 以 fixed 為定位點,初始化 ap 指向第一個可變參數
int val = va_arg(ap, int); // 取下一個 int 參數,ap 自動遞進
// ...
va_end(ap); // 清理 ap
}
va_list:不透明類型,實為指向棧幀參數區的指針(架構相關)。va_start(ap, last_fixed):以最後一個固定參數位址為基準計算可變參數起始位址。va_arg(ap, type):按 type 大小從棧取值,並將 ap 遞進對應位元組數(含對齊填充)。va_end(ap):某些架構需重置 ap,習慣調用。double average(int count, ...) {
va_list ap; va_start(ap, count);
double sum = 0;
for (int i = 0; i < count; i++) sum += va_arg(ap, double);
va_end(ap);
return sum / count;
}
int max_val(int count, ...) {
va_list ap; va_start(ap, count);
int max = INT_MIN;
for (int i = 0; i < count; i++) {
int v = va_arg(ap, int);
if (v > max) max = v;
}
va_end(ap);
return max;
}
void print_int(int n) { // 遞歸輸出整數
if (n < 0) { putchar('-'); n = -n; }
if (n / 10) print_int(n / 10);
putchar('0' + n % 10);
}
void my_printf(const char *fmt, ...) {
va_list ap; va_start(ap, fmt);
while (*fmt) {
if (*fmt != '%') { putchar(*fmt++); continue; }
fmt++;
switch (*fmt) {
case 'd': print_int(va_arg(ap, int)); break;
case 'c': putchar(va_arg(ap, int)); break; // char 提升為 int
case 's': {
const char *s = va_arg(ap, const char*);
while (*s) putchar(*s++);
break;
}
default: putchar(*fmt); break;
}
fmt++;
}
va_end(ap);
}
int main() {
my_printf("str=%s, char=%c, num=%d\n", "hello", 'X', 123);
// 輸出: str=hello, char=X, num=123
return 0;
}
va_arg 不檢查實際傳入類型,傳錯導致未定義行為。printf 格式字符串隱含、或首參數給計數)。float→double、char/short→int,va_arg 必須取提升後類型(double、int)。va_list 機制本質是棧幀參數區的指針走位。理解此原理,才能正確編寫可變參數函數、解讀 printf 族函數實現、避開類型不匹配、提升缺失等經典坑。