C 语言可变参数函数
更新时间:2026-08-26。本文是
languages/c/主题高手层的骨架文档(占位),完整展开将在后续批次补齐。
本文要回答的问题
printf("...", ...)凭什么能接任意多个参数?va_list/va_start/va_arg是怎么工作的?- 可变参数为什么"危险"?怎么写出安全的版本?
一、机制
函数声明用 ... 表示"还有更多参数":
c
#include <stdarg.h>
int my_sum(int n, ...) {
va_list ap;
va_start(ap, n); /* 从 n 之后开始取 */
for (int i = 0; i < n; i++) sum += va_arg(ap, int);
va_end(ap);
return sum;
}| 宏 | 作用 |
|---|---|
va_start(ap, last) | 初始化,定位第一个可变参数 |
va_arg(ap, Type) | 取一个 Type 参数并前进 |
va_end(ap) | 收尾清理 |
二、为什么危险
- 运行时才知道参数类型:
va_arg的类型要和你传的实际类型完全一致,编译器帮不上忙。 - 参数类型提升:
float变double、char变int,取参类型写错就乱读。 - 没有任何数量检查:取多了就读取栈上的垃圾,
printf("%d", 1, 2, 3)也合法——多传的忽略,少传的才崩。
三、安全用法
- 尽量用"格式化字符串"驱动(如 printf 风格),或第一个参数传数量/格式。
- 在可变函数之上再包一层:对固定数量参数写专用函数,只在边缘用可变参数。
- 编译期检查:gcc 的
__attribute__((format(printf, 1, 2)))让编译器校验格式串与实参。 - 传指针类型时注意对齐,别传值语义错误的对象。
四、与入门层的衔接
- printf 格式符的完整知识,见 printf 与格式输出。
- 可变参数的缓冲区溢出风险,见 缓冲区溢出与安全。
一句话总结
可变参数靠 va_list 在栈上"按声明类型取数",灵活但类型与数量全凭自觉:少传就崩、类型错就乱,用格式化串驱动 + 编译器 format 检查把它锁进安全区。
本文为骨架文档:核心结构已就位,示例代码与实测数据将在后续批次补齐。