stdarg 宏
va_list、va_start、va_arg
stdarg 宏 是 CoddyKit 上的免费 C Academy 课时。 这是第 1 节课,共 4 节。 你可以在下方免费阅读本课时的完整内容 — 然后在浏览器中使用内置代码编辑器和全天候 AI 导师进行实践。 这是 C Academy 学习路径的一部分,你的进度在网页和 CoddyKit 应用中同步。 C Academy 课程共包含 4 节课。
什么是可变参数函数
可变参数函数可以接受数量不固定的参数。您已经使用过一个:printf 可以接收任意数量的值。
在 C 中,您可以使用 <stdarg.h> 宏编写自己的可变参数函数。
#include <stdio.h>
int main(void) {
printf("%d %d %d\n", 1, 2, 3);
printf("%s\n", "any count of args");
return 0;
}省略号
声明可变参数函数时,将 ...(省略号)作为最后一个参数。
在它之前至少必须有一个命名参数,这样函数才能知道可变部分从哪里开始。
#include <stdio.h>
#include <stdarg.h>
void demo(int count, ...) {
printf("This function takes %d extra args\n", count);
}
int main(void) {
demo(3, 10, 20, 30);
return 0;
}包含 stdarg.h
要读取可变参数,需要使用 #include <stdarg.h>。
它定义了 va_list 类型以及 va_start、va_arg 和 va_end 宏。
#include <stdio.h>
#include <stdarg.h>
void show(int n, ...) {
va_list args;
va_start(args, n);
va_end(args);
printf("set up and cleaned up\n");
}
int main(void) {
show(2, 5, 6);
return 0;
}va_list 和 va_start
声明一个 va_list 变量,用于跟踪当前位置。然后调用 va_start(list, last),并传入最后一个命名参数。
这会让参数列表指向第一个可变参数。
#include <stdio.h>
#include <stdarg.h>
void first(int count, ...) {
va_list args;
va_start(args, count);
int value = va_arg(args, int);
printf("first variadic arg = %d\n", value);
va_end(args);
}
int main(void) {
first(1, 99);
return 0;
}va_arg:读取值
va_arg(list, type) 会返回下一个参数,并将列表向前移动。您必须告诉它要读取的确切类型。
每次调用都会向前移动一个参数。
#include <stdio.h>
#include <stdarg.h>
void three(int n, ...) {
va_list args;
va_start(args, n);
int a = va_arg(args, int);
int b = va_arg(args, int);
int c = va_arg(args, int);
printf("%d %d %d\n", a, b, c);
va_end(args);
}
int main(void) {
three(3, 7, 8, 9);
return 0;
}va_end:清理
读取完成后,请务必调用 va_end(list)。它会执行平台所需的清理工作。
在某些系统上,忘记调用它会导致未定义行为。
#include <stdio.h>
#include <stdarg.h>
int add_two(int dummy, ...) {
va_list args;
va_start(args, dummy);
int sum = va_arg(args, int) + va_arg(args, int);
va_end(args);
return sum;
}
int main(void) {
printf("%d\n", add_two(0, 4, 6));
return 0;
}了解参数数量
这些宏无法告诉您传入了多少个参数。您必须自行传递这个信息。
一种常见做法是添加一个前置的计数参数,例如 sum(3, a, b, c)。
#include <stdio.h>
#include <stdarg.h>
int sum(int count, ...) {
va_list args;
va_start(args, count);
int total = 0;
for (int i = 0; i < count; i++)
total += va_arg(args, int);
va_end(args);
return total;
}
int main(void) {
printf("%d\n", sum(4, 1, 2, 3, 4));
return 0;
}使用哨兵值
标记结尾的另一种方法是使用哨兵:调用者必须传入一个特殊的终止值,例如 -1 或 NULL。
循环读取,直到读到哨兵值为止。
#include <stdio.h>
#include <stdarg.h>
int sum_until_neg(int first, ...) {
va_list args;
va_start(args, first);
int total = first, v;
while ((v = va_arg(args, int)) != -1)
total += v;
va_end(args);
return total;
}
int main(void) {
printf("%d\n", sum_until_neg(10, 20, 30, -1));
return 0;
}默认参数提升
可变参数会经历默认提升:char 和 short 会变成 int,而 float 会变成 double。
因此,使用 va_arg 时,请始终将小整数读取为 int,将浮点数读取为 double。
#include <stdio.h>
#include <stdarg.h>
void show_double(int n, ...) {
va_list args;
va_start(args, n);
double d = va_arg(args, double);
printf("%.2f\n", d);
va_end(args);
}
int main(void) {
show_double(1, 3.5f);
return 0;
}复制 va_list
如果需要遍历参数两次,请在读取前使用 va_copy(dest, src) 复制参数列表。
调用 va_end 后,不能重新使用 va_list,必须通过 va_start 重新开始。
#include <stdio.h>
#include <stdarg.h>
void twice(int n, ...) {
va_list a, b;
va_start(a, n);
va_copy(b, a);
printf("%d %d\n", va_arg(a, int), va_arg(b, int));
va_end(a);
va_end(b);
}
int main(void) {
twice(1, 42);
return 0;
}计算平均值
将计数模式与浮点数读取结合起来,我们就可以计算以可变参数形式传入的一组数字的平均值。
#include <stdio.h>
#include <stdarg.h>
double average(int count, ...) {
va_list args;
va_start(args, count);
double sum = 0;
for (int i = 0; i < count; i++)
sum += va_arg(args, double);
va_end(args);
return sum / count;
}
int main(void) {
printf("%.2f\n", average(3, 2.0, 4.0, 6.0));
return 0;
}快速检查
测试您对 stdarg 宏的理解。
回顾
您学习了可变参数机制:
- 至少有一个命名参数后,使用
...进行声明。 va_start(list, last)开始读取,va_arg(list, type)读取参数,va_end(list)执行清理。- 这些宏不知道参数数量——请使用计数参数或哨兵值。
- 注意默认提升:将小整数读取为
int,将浮点数读取为double。
常见问题解答
「stdarg 宏」课时是免费的吗?
是的 — 「stdarg 宏」的完整文本可在网页上免费阅读。要进行交互式练习(内置代码编辑器和全天候 AI 导师)并解锁 C Academy 课程的其余内容,请升级到 CoddyKit PRO。 C Academy 课程共包含 4 节课。
「stdarg 宏」这节课中我会学到什么?
va_list、va_start、va_arg 你通过在浏览器中直接运行的动手代码来练习 C Academy,全天候 AI 导师会在你学习这节课的过程中回答你的问题。
学习 C Academy 需要有经验吗?
无需任何先前经验。CoddyKit 上的 C Academy 课程适合初学者到高级学习者,你可以从这里开始或从头开始,按照自己的节奏学习。 这是第 1 节课,共 4 节。
「stdarg 宏」课时需要多长时间?
大多数 CoddyKit 课程大约需要 5–10 分钟。每节课都很精短且互动,所以你能稳步进步,并在网页和应用中从离开的地方继续。
我能在这节 C Academy 课中编写并运行代码吗?
能。每节 C Academy 课都包含内置代码编辑器,你可以在浏览器中直接编写并运行真实代码,并获得即时 AI 反馈 — 无需本地设置。
此课程中的所有课时
- stdarg 宏
- 编写类似 printf 的函数
- 类型安全问题
- 实际示例