int printf(const char *format, ...);
省略號 ... 表示參數個數與類型不固定,由格式串運行時解析決定。
<stdarg.h> 提供四個核心宏:
va_list ap; —— 參數列表指針(實為 char*)va_start(ap, last_fixed_param); —— 初始化指向首個可變參數va_arg(ap, type); —— 取出下一參數並推進指針va_end(ap); —— 清理(通常為空)#include <stdio.h>
#include <stdarg.h>
#include <unistd.h>
#include <string.h>
void myprintf(const char *fmt, ...) {
va_list ap;
va_start(ap, fmt);
char buf[64];
while (*fmt) {
if (*fmt == '%') {
fmt++;
switch (*fmt) {
case 'd':
case 'u': {
int val = va_arg(ap, int);
sprintf(buf, "%d", val);
write(STDOUT_FILENO, buf, strlen(buf));
break;
}
case 'f': {
double val = va_arg(ap, double); // float 會提升為 double
sprintf(buf, "%f", val);
write(STDOUT_FILENO, buf, strlen(buf));
break;
}
default:
write(STDOUT_FILENO, fmt-1, 2); // 原樣輸出 %?
}
} else {
write(STDOUT_FILENO, fmt, 1);
}
fmt++;
}
va_end(ap);
}
int main(void) {
myprintf("int=%d, uint=%u, float=%f\n", -1, 5, 0.2);
return 0;
}
輸出:int=-1, uint=5, float=0.200000
typedef char *va_list;
#define _AUPBND (sizeof(acpi_native_int) - 1) // 對齊邊界
#define _ADNBND (sizeof(acpi_native_int) - 1)
#define _bnd(X, bnd) (((sizeof(X)) + (bnd)) & (~(bnd))) // 向上對齊
#define va_start(ap, A) (void)((ap) = ((char *)&(A) + _bnd(A, _AUPBND)))
#define va_arg(ap, T) (*(T *)(((ap) += _bnd(T, _AUPBND)) - _bnd(T, _ADNBND)))
#define va_end(ap) (void)0
A 的地址,加上其對齊後大小,得到首個可變參數地址 → ap。ap += _bnd(T, _AUPBND):指針前移至下一參數起始位置(已對齊)。- _bnd(T, _ADNBND):回退到當前參數實際數據起始處。*(T*):按類型 T 解引用讀取。double 佔 8 字節,va_arg(ap, double) 會一次跨越兩個棧槽。va_start 能精確定位。若為左→右或被調用者清棧,均無法滿足上述條件。
va_arg(ap, float) 錯誤!float 會默認提升為 double,必須用 va_arg(ap, double)。va_start 定位錨點。va_copy(dest, src) (C99),而非直接賦值。