C++ --- 可变参数之格式化字符串
C语言中对可变参数的处理方法声明在头文件#include <stdarg.h>中,主要使用以下几个方法:
1、va_list
va_list表示可变参数列表类型,实际上就是一个char指针
2、va_start
va_start用于获取函数参数列表中可变参数的首指针(获取函数可变参数列表)
* 输出参数ap(类型为va_list): 用于保存函数参数列表中可变参数的首指针(即,可变参数列表)
* 输入参数A: 为函数参数列表中最后一个固定参数
* 输出参数ap(类型为va_list): 用于保存函数参数列表中可变参数的首指针(即,可变参数列表)
* 输入参数A: 为函数参数列表中最后一个固定参数
3、va_arg
va_arg用于获取当前ap所指的可变参数并将并将ap指针移向下一可变参数
* 输入参数ap(类型为va_list): 可变参数列表,指向当前正要处理的可变参数
* 输入参数T: 正要处理的可变参数的类型
* 返回值: 当前可变参数的值
* 输入参数ap(类型为va_list): 可变参数列表,指向当前正要处理的可变参数
* 输入参数T: 正要处理的可变参数的类型
* 返回值: 当前可变参数的值
在C/C++中,默认调用方式_cdecl是由调用者管理参数入栈操作,且入栈顺序为从右至左,入栈方向为从高地址到低地址。因此,第1个到第n个参数被放在地址递增的堆栈里。所以,函数参数列表中最后一个固定参数的地址加上第一个可变参数对其的偏移量就是函数的可变参数列表了(va_start的实现);当前可变参数的地址加上下一可变参数对其的偏移量的就是下一可变参数的地址了(va_arg的实现)。这里提到的偏移量并不一定等于参数所占的字节数,而是为参数所占的字节数再扩展为机器字长(acpi_native_int)倍数后所占的字节数(因为入栈操作针对的是一个机器字),这也就是为什么_bnd那么定义的原因。
4、va_end
va_end用于结束对可变参数的处理。实际上,va_end被定义为空.它只是为实现与va_start配对(实现代码对称和"代码自注释"功能)
对可变参数列表的处理过程一般为:
1、用va_list定义一个可变参数列表
2、用va_start获取函数可变参数列表
3、用va_arg循环处理可变参数列表中的各个可变参数
4、用va_end结束对可变参数列表的处理
例子:
#include <stdarg.h> #include <iostream> #define LENGTH 128 void TestArg(const char *fmt, ...); //添加函数,行数等信息 #define LOG(msg, ...) TestArg("%s::(%d): " msg, __FUNCTION__, __LINE__, ##__VA_ARGS__); // void subfunc(va_list argp) // { // T arg = va_arg(argp, T); /* 从argp中逐一取出所要的参数,这里需要知道每一个参数的类型T */ // } //处理可变参数 void TestArg(const char *fmt, ...) { //TestArg(fmt); 可变参数不能像这样二次传递 //可变参数处理 va_list args; va_start(args, fmt); //可以用vsnprintf,获取可变参数的长度 const auto len = vsnprintf(nullptr, 0, fmt, args); //注意,args只能用一次。如果还要再用,必须重新调用va_start va_end(args); //TestArg(fmt); 可变参数不能像这样二次传递 //subfunc(args); //可以类似于这样传递 char buffer[LENGTH] = {0}; va_start(args, fmt); vsnprintf(buffer, LENGTH, fmt, args); va_end(args); printf("%s", buffer); //this is test. 1, 2, happy } int main(int argc, char **argv) { TestArg("this is test. %d, %d, %s\n", 1, 2, "happy"); LOG("happy %d\n", 3); //main(741): happy 3 return 0; }
C++中可以使用模板处理可变参数
template <typename... Args> std::string show_str(const char *pformat, Args... args) { // 计算字符串长度 int len_str = std::snprintf(nullptr, 0, pformat, args...); if (0 >= len_str) return std::string(""); len_str++; char *pstr_out = nullptr; pstr_out = new (std::nothrow) char[len_str]; // 申请失败 if (nullptr == pstr_out) return std::string(""); // 构造字符串 std::snprintf(pstr_out, len_str, pformat, args...); // 保存构造结果 std::string str(pstr_out); // 释放空间 delete[] pstr_out; pstr_out = nullptr; return str; }
参考:https://blog.csdn.net/stephenbruce/article/details/124579419