#define _INTSIZEOF(n) ((sizeof(n) + sizeof(int) - 1) & ~(sizeof(int) - 1)) // 對齊到 int 倍數
#define va_start(ap, v) (ap = (va_list)&v + _INTSIZEOF(v)) // 定位第一個可變參數位址
#define va_arg(ap, t) (*(t *)((ap += _INTSIZEOF(t)) - _INTSIZEOF(t))) // 取值並前進
#define va_end(ap) (ap = (va_list)0) // 置空
typedef char *va_list; // 指向棧上參數的指標
ESP 指向棧頂 (最後壓入的第一個參數)。push 導致 ESP 減小。v 在棧上,其上方 (位址較高) 緊接著可變參數。va_arg 魔法#define va_arg(ap, t) (*(t *)((ap += _INTSIZEOF(t)) - _INTSIZEOF(t)))
假設 ap 當前指向參數 N 起始位址:
ap += _INTSIZEOF(t):ap 前進到下一個參數 N+1 起始位址 (完成「前進」動作)。(ap - _INTSIZEOF(t)):運算結果回到參數 N 起始位址 (取值位址)。(t *)...:轉型為型別 t 指標。*...:解引用取得值。精妙點:單一運算式同時完成「取得當前值」與「推進到下一參數」,ap 狀態自動更新,支援連續 va_arg 調用。
#include <stdio.h>
#include <stdarg.h>
void simple_va_fun(int fixed, ...) {
va_list ap;
va_start(ap, fixed); // 以最後一個固定參數為定位點
int arg1 = va_arg(ap, int); // 取第一個可變參數
int arg2 = va_arg(ap, int); // 取第二個
va_end(ap); // 清理
printf("fixed=%d, arg1=%d, arg2=%d\n", fixed, arg1, arg2);
}
int main() {
simple_va_fun(100, 200); // 只給 1 個可變參數,arg2 讀到垃圾值
simple_va_fun(100, 200, 300); // 正確
return 0;
}
警告:C 語言不檢查可變參數個數與型別。呼叫端需自行約定協定 (如 printf 靠格式字串解析)。
#define MY_MAX(...) max(__VA_ARGS__)
// 使用:MY_MAX(1,2,3,4) → max(1,2,3,4)
編譯器支援度:GCC/Clang 完美,MSVC 2013+ 支援,VC6.0 不支援。
_INTSIZEOF 處理對齊;va_arg 單一運算式完成取值+推進。va_start(ap, last_fixed)。