在C语言中,当我们定义一个可变参数的函数时,我们需要一种方法来依次访问每个参数。va_start
和va_end
宏就是用来处理这些可变参数的。
va_start
宏的作用是初始化一个va_list
类型的变量,这个变量是用来存储和访问可变参数的。va_start
应该在函数中对其它参数做任何处理之前调用。要使用va_start
宏,你需要两个参数:首先是你定义的va_list
类型变量,其次是函数的最后一个非可变参数。你可以把它理解为告诉va_list
变量从哪里开始获取可变参数列表。
下面是va_start
宏的一个示例:
#include <stdarg.h>// 假设format是最后一个非可变参数
va_list args;
va_start(args, format);
当你完成了可变参数的处理,并且不再需要va_list
类型变量后,你应该使用va_end
宏。va_end
宏的作用是清理赋予va_list
变量的任何资源。虽然在很多实现中,va_end
可能仅仅是一个空操作,但它的使用是标准要求的好习惯,可以保证代码的可移植性。
va_end
宏是这样使用的:
va_end(args);
它需要va_list
类型变量作为参数,并且应该与每一个va_start
宏的调用相匹配。
在可变参数的函数中,format
并不是一个特定的变量名,它一般代表函数中最后一个固定的参数,用来告诉va_start
宏从哪个参数之后开始处理可变参数列表。
理解这个概念可能需要看一个例子。假设我们有一个函数print_formatted
,它的第一个参数是一个格式字符串(类似于printf
函数中的格式字符串),后面可以跟任意多的参数。这个格式字符串就是我们所说的“最后一个非可变参数”。
例如:
void print_formatted(const char *format, ...) {va_list args;va_start(args, format); // 这里,format 是最后一个固定参数// ...va_end(args);
}
在这个例子中,format
的角色就是告诉va_start
:可变参数开始于format
参数之后,因为format
是在省略号...
之前定义的最后一个参数。这样,当我们在函数内部使用va_list
遍历参数时,它会跳过format
,从紧随其后的第一个可变参数开始处理。
简而言之,format
就是函数签名中,紧邻可变参数省略号...
左边的那个参数。这个“格式参数”只是一个占位符,它在不同函数中可以有不同的名字,关键在于它是可变参数之前的最后一个参数。
void dump(void* buf_src, int len, const char *format, ...)
{if (buf_src == NULL || len <= 0){return;}va_list args, args_copy;va_start(args, format);va_copy(args_copy, args);int required_len = vsnprintf(NULL, 0, format, args) + 1;va_end(args);char *str_buf = (char *)malloc(required_len);if (str_buf == NULL){va_end(args_copy);return;}vsnprintf(str_buf, required_len, format, args_copy);va_end(args_copy);char *data_buf = (char *)malloc(len * 3);if (data_buf == NULL){free(str_buf);return;}unsigned char *byte_src = (unsigned char *)buf_src;for (int i = 0; i < len; i++) {snprintf(&data_buf[3 * i], 4, "%02X ", byte_src[i]);}data_buf[len * 3 - 1] = '\0';printf("%s:%s\n", str_buf, data_buf);free(str_buf);free(data_buf);
}