printf() 为什么能想传几个就传几个参数?因为它是变参函数(variadic function),参数个数不固定。自己写一个也不难,关键是搞懂 stdarg.h 里的四个家伙:va_list、va_start、va_arg、va_end。
什么是变参函数
变参函数指参数数量不固定的函数。它至少有一个固定参数,后面跟三个点 ...,表示接下来想传多少传多少:
return_type name(fixed_arg, ...);四个必须认识的宏
访问变参要在 stdarg.h 里取工具,固定四步走:
- 1. va_list
- 声明一个参数列表变量,用来暂存到哪里去取参数的信息。
va_list list;- 2. va_start()
- 初始化列表,第二个参数是最后一个固定参数,它标记变参从哪开始。
va_start(list, fixed_arg);- 3. va_arg()
- 取出下一个变参,第二个参数写你要的类型。取几次就调几次,不能超数量。
va_arg(list, type);- 4. va_end()
- 收尾清理,释放列表相关的资源。
va_end(list);第一个例子:打印任意多个整数
第一个参数 n 告诉函数后面还有几个数,然后循环取出来打:
#include <stdio.h>
#include <stdarg.h>
/* 变参函数:打印 n 个整数 */
void print(int n, ...) {
va_list args;
va_start(args, n);
for (int i = 0; i < n; i++)
printf("%d ", va_arg(args, int));
printf("\n");
va_end(args);
}
int main() {
/* 同一个函数,参数数量随便变 */
print(3, 1, 2, 3);
print(5, 1, 2, 3, 4, 5);
return 0;
}输出:
1 2 3
1 2 3 4 5注意这里的妙处:固定参数 n 就是变参的数量。变参函数不知道你传了几个,所以数量必须由调用方通过某种方式告诉它,要么像这样传个数,要么用终止标记(比如读到 0 结束),要么靠格式串(printf 就是靠 % 个数)。
第二个例子:求任意多个数的和
#include <stdarg.h>
#include <stdio.h>
/* 变参函数:求 n 个数的和 */
int getSum(int n, ...) {
int sum = 0;
va_list list;
va_start(list, n);
for (int i = 0; i < n; i++)
sum += va_arg(list, int);
va_end(list);
return sum;
}
int main() {
printf("1 + 2 = %d\n", getSum(2, 1, 2));
printf("3 + 4 + 5 = %d\n", getSum(3, 3, 4, 5));
printf("6 + 7 + 8 + 9 = %d\n", getSum(4, 6, 7, 8, 9));
return 0;
}输出:
1 + 2 = 3
3 + 4 + 5 = 12
6 + 7 + 8 + 9 = 30第三个例子:混合类型的变参
变参可以混类型,但取的时候类型要和你传的一致(va_arg 的类型写错是未定义行为)。用位置信息区分类型:
#include <stdio.h>
#include <stdarg.h>
/* 交替打印整数和浮点数 */
void print(int n, ...) {
va_list args;
va_start(args, n);
for (int i = 0; i < n; i++) {
if (i % 2 == 0) {
printf("Integer: %d\n", va_arg(args, int));
} else {
printf("Float: %.2f\n", va_arg(args, double));
}
}
va_end(args);
}
int main() {
print(4, 10, 3.14, 20, 2.71);
return 0;
}输出:
Integer: 10
Float: 3.14
Integer: 20
Float: 2.71注意事项
- va_arg 取出的类型必须和传进去的一致,尤其 float 在变参里会提升成 double,得用 double 取。
- 别取超过实际传入的数量,那是未定义行为。
- 每个 va_start 必须配一个 va_end。
变参函数是 printf 家族和很多日志库的基础,自己实现一个简单的"迷你 printf"是巩固理解的好练习。