0Pricing
C Academy · 课时

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 反馈 — 无需本地设置。

此课程中的所有课时

  1. stdarg 宏
  2. 编写类似 printf 的函数
  3. 类型安全问题
  4. 实际示例
← 返回 C Academy