printf、scanf 等函數如何支持不定數量參數?C 標準庫提供 <stdarg.h> 宏(va_start、va_arg、va_end、va_copy)屏蔽平台差異,實現可移植的可變參數函數。本文詳解原理與用法。
#include// 1. 聲明 va_list 變量 va_list ap; // 2. 初始化:從最後一個固定參數開始 va_start(ap, last_fixed_param); // 3. 依次獲取參數:指定類型,宏自動推進指針 int x = va_arg(ap, int); double y = va_arg(ap, double); char *s = va_arg(ap, char*); // 4. 結束:清理棧帧(某些架構必須) va_end(ap); // 5. 多次遍歷需復制 va_list ap2; va_copy(ap2, ap); // C99 // ... 使用 ap2 ... va_end(ap2);
// 固定第一參數為參數個數
int Sum(int Num, ...) {
int S = 0, T;
va_list ap;
va_start(ap, Num);
for (int i = 0; i < Num; i++) {
T = va_arg(ap, int);
S += T;
}
va_end(ap);
return S;
}
void PrintStrings(int Num, ...) {
va_list ap;
va_start(ap, Num);
for (int i = 0; i < Num; i++) {
char *s = va_arg(ap, char*);
printf("%s\n", s);
}
va_end(ap);
}
int main() {
printf("Sum = %d\n", Sum(10, 1,2,3,4,5,6,7,8,9,10)); // 55
PrintStrings(2, "Hello", "World");
return 0;
}
char* 指向當前參數在棧上的地址。&last_fixed + sizeof(last_fixed)(考慮對齊)。*(type*)((ap += _INTSIZEOF(type)) - _INTSIZEOF(type))。va_arg **無運行時類型檢查**,傳入 float 卻用 va_arg(ap, double) 會導致棧誤位(因默認參數提升 float→double)。調用端必須嚴格匹配類型,通常約定:float 傳 double,char/short 傳 int。
Sum(int Num, ...)。execl(path, arg0, arg1, ..., NULL) 以 NULL 結束。printf(fmt, ...) 解析 % 佔位符確定數量類型。C++ 變參模板 template 提供**編譯期類型安全、參數包展開、完美轉發**,完全優於 C 的運行時機制。現代 C++ 應優先使用。
<stdarg.h>**,使用標準宏,勿自造指針算術。va_start 錨點。va_arg,注意默認提升規則。va_start/va_end**,缺一不可。va_copy**(C99),勿重用同一 va_list。