忻州市网站建设_网站建设公司_后端开发_seo优化
2026/1/21 18:13:42 网站建设 项目流程

C99 va_list 可变参数

std::va_list - cppreference.cn - C++参考手册

C99或C++11开始,头文件 stdarg.h 中声明了类型 va_list 和4个宏:va_startva_argva_endva_copy

va_list 类型存储了这4个宏进行操作所需的信息,本质是指向参数栈的一个指针/结构体。因此,再跨函数调用之间使用 va_list 时,需要保证:当一个 va_list 实例在函数 Foo 中被创建后传递给函数 Bar 并在 Bar 函数中对其使用了 va_arg 后,Foo 函数中后续再使用这个 va_list 前必须先调用 va_end

这个类型和对应的操作都是编译器内部实现的。

操作

va_start

原型:

void va_start(va_list ap, last);

功能:初始化一个 va_list 类型的变量 ap

参数:

  • ap 是一个 va_list 类型的变量,实际是一个指针,指向可变参数列表的第一个参数;
  • last 是可变参数列表 ... 之前的最后具名一个参数。因此被调用函数一定知道这个参数的类型,且这个参数不能声明为register,也不能是一个函数或数组。同时,这个参数还可以用于向函数传递一些信息,比如元素的个数,格式化字符串等。

说明:显然根据函数原型,使用 va_start 的函数如果不是直接以 va_list 作为参数,则其函数签名一定类似:

ret_type func(type1 arg1, type2 arg2, ..., typeN last_fixed_arg, ...) {va_list ap;va_start(ap, last_fixed_arg); // arg1,arg2表示可变参数前面都是固定参数。last_fixed_arg则表示要用可变参数,则必然是一个固定类型的实参作为last,然后跟着可变参数,可变参数的类型不必与last_fixed_arg的类型相同。// ...va_end(ap);
}

示例:

#include <cstdarg>
#include <iostream>int add_nums(int count...)
{int result = 0;std::va_list args;va_start(args, count);for (int i = 0; i < count; ++i)result += va_arg(args, int);va_end(args);return result;
}int main()
{std::cout << add_nums(4, 25, 25, 50, 50) << '\n';
}

va_arg

原型:

type va_arg(va_list ap, type);

功能:从已初始化的 va_list 变量中取出下一个参数,并返回其值。会自动将 ap 指针向后移动来指向下一个参数。

参数:ap 是一个已初始化的 va_list 变量;type 是类型名,表示期望取出的参数的数据类型,如 intdouble 等。

说明:如果实际传入的参数和 type 指定的类型不兼容,或者 ap 中已经没有更多参数了,则是未定义行为。

示例:

#include <math.h>
#include <stdarg.h>
#include <stdio.h>double stddev(int count, ...)
{double sum = 0;double sum_sq = 0;va_list args;va_start(args, count);for (int i = 0; i < count; ++i){double num = va_arg(args, double);sum += num;sum_sq += num*num;}va_end(args);return sqrt(sum_sq / count - (sum / count) * (sum / count));
}int main(void)
{printf("%f\n", stddev(4, 25.0, 27.3, 26.9, 25.7));
}

va_end

void va_end(va_list ap);

功能:结束对可变参数列表的遍历,并执行必要的清理工作。

参数:ap 是需要清理的 va_list 变量。

说明:每一个 va_start 都必须有匹配的 va_end ,在调用完 va_end 后,ap 就是未定义的了,无法直接再次使用。

va_copy

void va_copy(va_list dest, va_list src);

功能:复制一个 va_list

参数:dest 是目标,作为 src 的副本;src 是源,被拷贝。

说明:因为 va_list 只能遍历一次,在需要多次遍历同一个可变参数列表时,可以先用 va_copy 创建一个副本。另外,使用 va_copy 初始化的 va_list ,最后也需要使用单独的 va_end 进行清理。

#include <stdarg.h>
#include <stdio.h>
#include <time.h>void debug_log(const char* fmt, ...)
{struct timespec ts;timespec_get(&ts, TIME_UTC);char time_buf[100];size_t rc = strftime(time_buf, sizeof time_buf, "%D %T", gmtime(&ts.tv_sec));snprintf(time_buf + rc, sizeof time_buf - rc, ".%06ld UTC", ts.tv_nsec / 1000);va_list args1;va_start(args1, fmt);va_list args2;va_copy(args2, args1);char buf[1+vsnprintf(NULL, 0, fmt, args1)];va_end(args1); // args1 对应的清理vsnprintf(buf, sizeof buf, fmt, args2);va_end(args2); // args2 对应的清理printf("%s [debug]: %s\n", time_buf, buf);
}int main(void)
{debug_log("Logging, %d, %d, %d", 1, 2, 3);
}

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询