__VA_ARGS
变长参数的宏定义是指在宏定义中参数列表的最后一个参数为省略号,而预定义宏__VA_ARGS
则可以在宏定义的实现部分替换省略号所代表的字符串,比如:
#define PR(...) printf(__VA_ARGS__)
就可以定义一个printf的别名PR。实际上,变长参数宏与printf经常搭配使用:
#include
#define LOG(...){ \
fprintf(stderr, "%s: Line %d:\t", __FILE__, __LINE__);\
fprintf(stderr, __VA_ARGS__);\
fprintf(stderr, "\n");\
}
int main()
{
LOG("test : %d", 3);
return 0;
}
头文件:
<stdarg.h>
作用:
保存va_start,va_arg,va_end和va_copy(typedef)所需的信息
va_start | 允许访问可变参数函数参数 |
va_arg | 访问下一个可变参数函数参数 |
va_copy | 制作可变参数函数参数(函数宏)的副本 |
va_end | 结束可变参数函数参数(函数宏)的遍历 |
当你的函数的参数个数不确定时,就可以使用上述宏进行动态处理,这无疑为你的程序增加了灵活性。
va_list的使用方法:
在使用va_list是应该注意一下问题:
C++11在标准库中提供了initializer_list
类,用于处理参数数量可变但是类型相同的情况。使用initializer_list
最常用的方式是通过{}
包围的值列表对其进行初始化:
initializer_list<int> vlist{9, 8, 7, 6};
继续看下面的函数:
template<typename T>
void output(initializer_list<T> lst)
{
for(auto &a : lst){
cout << a << endl;
}
}
这个函数很简单,就是输出list中的内容,它有几个特点:
函数弄好以后,怎么使用就可以看心情了。
initializer_list<int> vlist{9, 8, 7, 6};
output(vlist);
output({1, 3, 4, 5});
output({"How", "are", "you", "!"});
#include
#include
using namespace std;
template<class T>
T add(int n, T t...) //第一个表示多少个参数,最后
{
cout << typeid(T).name() << endl;
va_list arg_ptr; //开头指针 //va_list:char *类型
va_start(arg_ptr, n); //从arg_ptr开始读取N个
T res(0); //初始化为0
for (int i = 0; i < n; i++)
{
res += va_arg(arg_ptr, T); //根据数据类型取出数据
}
va_end(arg_ptr);
//cout << res << endl;
return res;
}
void main()
{
cout << add(1, 2, 3) << endl;
cout << add(1.1, 2.2, 3.3, 4.4) << endl;
cin.get();
}
//参数类型不一致,个数不确定
#include
#include
using namespace std;
void show() //递归必须有一个结束符:空函数
{
}
template <typename T, typename...Args> //typename比class作用域更强
//typename...处理可变参数
void show(T t, Args...args)
{
cout << t << endl; //打印
show(args...);
}
void main()
{
show(1, 1.2, "123", "A");
cin.get();
}
总结:
1、CPP的可变参数库:#include < cstdarg>
2、尽量使用typename而不是class来声明一个模板
3、函数的可变参数模板必须用递归来实现
#include
#include
using namespace std;
void show(const char *str)
{
cout << str;
}
template <typename T, typename...Args> //typename比class作用域更强
void show(const char *str, T t, Args...args)
{
while (str && *str) //指针不为空为字符串没有指向末尾
{
if (*str == '%' && *(str + 1) != '%')
{
++str; //指针向前移动
cout << t;
show(++str, args...); //继续调用
return;
}
else
{
cout << *str++; //跳过一个字符
}
}
}
void main()
{
printf("%dADCE1345%s%c%%XXXX", 10, "1234", '0');
putchar('\n');
show("%d1345%s%c%%XXXX", 10, "1234", '0');
cin.get();
}
| | |
|--|--|
| | |
typedef char *va_list;
va_start宏,获取可变参数列表的第一个参数的地址(list是类型为va_list的指针,param1是可变参数最左边的参数):
#define va_start(list,param1) ( list = (va_list)¶m1+ sizeof(param1) )
va_arg宏,获取可变参数的当前参数,返回指定类型并将指针指向下一参数(mode参数描述了当前参数的类型):
#define va_arg(list,mode) ( (mode *) ( list += sizeof(mode) ) )[-1]
va_end宏,清空va_list可变参数列表:
#define va_end(list) ( list = (va_list)0 )
假设有这样一个函数
void test(char *para1,char *param2,char *param3, char *param4)
{
va_list list;
......
return;
}
在c语言中,函数参数是存储在栈中的,函数参数从右到左依次入栈。又Linux中,栈地址从高到低生长。因此,调用test
函数时,其参数入栈情况如下:
调用va_start(list, param1)
时,list
指针指向情况如下:
最复杂的宏是va_arg。它必须返回一个由va_list所指向的恰当的类型的数值,同时递增va_list,使它指向参数列表中的一个参数(即递增的大小等于与va_arg宏所返回的数值具有相同类型的对象的长度)。因为类型转换的结果不能作为赋值运算的目标,所以va_arg宏首先使用sizeof来确定需要递增的大小,然后把它直接加到va_list上,这样得到的指针再被转换为要求的类型。因为该指针现在指向的位置"过"了一个类型单位的大小,所以我们使用了下标-1来存取正确的返回参数。
#include
#include
void simple_printf(const char *fmt, ...){
va_list args; // //定义一个具有va_list型的变量,这个变量是指向参数的指针。
va_start(args, fmt); // 第一个参数指向可变列表的地址,地址自动增加
while (*fmt != '\0'){
if (*fmt == 'd'){
int i = va_arg(args, int ); // 访问下一个可变参数函数参数
printf("%d\n", i);
} else if (*fmt == 'c'){
int c = va_arg(args, int);
printf("%c\n", c);
}else if (*fmt == 'f') {
double d = va_arg(args, double);
printf("%f\n", d);
}
++fmt;
}
}
int main(){
simple_printf("dcff", 3, 'a', 1.99, 425.5);
}
#include
#include
#include
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(){
printf("%f\n", stddev(4, 25.0, 27.3, 26.9, 25.7));
}
#include
#include
double stddev(int count, ...){
double sum = 0;
va_list args1, args2;
va_start(args1, count);
va_copy(args2, args1);
for (int i = 0; i < count; ++i) {
double num = va_arg(args1, double);
sum += num;
}
for (int i = 0; i < count; ++i) {
double num = va_arg(args2, double);
sum += num;
}
va_end(args1);
return sum;
}
int main(){
printf("%f\n", stddev(4, 25.0, 27.3, 26.9, 25.7));
}
#include
#include
void error(char *format, ...)
{
va_list ap;
va_start(ap, format);
fprintf(stderr, "Error: ");
vfprintf(stderr, format, ap);
va_end(ap);
fprintf(stderr, "\n");
return;
}
·
#include
#include
#include
void acl_msg_printf(const char *fmt,...){
char buf[2048];
va_list ap;
va_start (ap, fmt);
vsnprintf(buf, sizeof(buf), fmt, ap);
printf("%s\r\n", buf);
va_end(ap);
}
int main(){
acl_msg_printf("%s %d %s", "Failed", 100, "times");
}
#include
#include
#include
void acl_msg_error(char *format, ...)
{
va_list ap;
va_start(ap, format);
fprintf(stderr, "Error: ");
vfprintf(stderr, format, ap);
va_end(ap);
fprintf(stderr, "\n");
return;
}
int main(){
acl_msg_error("%s %d %s", "Failed", 100, "times");
}
include <cstdio>
#include
#include
#include
#include
std::string str_format(const char *fmt, ...)
{
int old_size = strlen(fmt);
std::unique_ptr<char[]> buf(new char[old_size]);
va_list ap;
va_start(ap, fmt);
int new_size = vsnprintf(buf.get(), old_size, fmt, ap);
va_end(ap);
if (new_size < 0)
return "";
buf.reset(new char[new_size + 1]);
va_start(ap, fmt);
new_size = vsnprintf(buf.get(), new_size + 1, fmt, ap);
va_end(ap);
if (new_size < 0)
return "";
return std::string(buf.get());
}
int main()
{
auto ret = str_format("%d %lf %s", 1, 3.14, "hello world");
printf("%s\n", ret.c_str()); // 1 3.140000 hello world
return 0;
}