printf(fmt, ...) 如何知道後續參數個數、類型、大小?答案在格式字符串 fmt 中。
#include
void my_printf(const char *fmt, ...) {
va_list ap;
va_start(ap, fmt); // ap 指向 fmt 之後第一個參數
char buf[1024];
int n = vsprintf(buf, fmt, ap); // 交給 vsprintf 解析 fmt 並從 ap 取值
va_end(ap);
write(1, buf, n);
}
typedef char *va_list;
#define _INTSIZEOF(n) ((sizeof(n) + sizeof(int) - 1) & ~(sizeof(int) - 1))
#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)
fmt 在最頂端。va_start 計算 fmt 地址 + 大小(對齊後)= 下一個參數地址。va_arg 根據類型 t 計算大小,移動指針並解引用取值。sizeof(int) 位元組(小類型提升為 int)。
/* 返回多個 int 參數的最大值,第一個參數指定個數 */
int Max(int n, ...) {
va_list ap;
va_start(ap, n);
int max = va_arg(ap, int);
for (int i = 1; i < n; i++) {
int v = va_arg(ap, int);
if (v > max) max = v;
}
va_end(ap);
return max;
}
/* 調用 */
int m = Max(3, 45, 12, 56); // 返回 56
printf("%d %f", 1, 2.0):%d 讀 int,%f 讀 double(float 提升)。-Wformat 可檢查格式字符串與參數匹配度。%f),否則讀取鄰近棧內容。