开发日志模块时__VA_ARGS__怎么用va_start,va_end,va_copy用法说明

一.__VA_ARGS__ 使用

 

在写log模块的时候,一般都会用到变长参数的处理,此时就会涉及到__VA_ARGS__宏,以及va_start, va_end,va_copy等函数来协助完成。

 

1.__VA_ARGS__只能用在宏定义中,用在函数中,gcc编译不过

2.要特别注意,如果边长参数为空的时候,在打印时会有额外逗号的问题

 

例如:

#define ILOG(fmt, …) \

“%s:%u:” fmt “\n”, \

__FUNCTION__, \

__LINE__, \

__VA_ARGS__

 

int main()

{

printf(ILOG(“%s-%d”, “string”, 1));

}

这样调用是没问题的,因为宏中fmt就是”%s-%d”,后面的边长参数是”hello world”, 1

可以使用gcc -E main.c -o a.out,可以看到a.out中预处理后结果为

int main()

{

printf(“%s:%u:” “%s-%d” “\n”, __FUNCTION__, 9, “string”, 1);

}

 

但是上面的调用一旦直接打印字符串就出问题了。

例如:

int main()

{

printf(ILOG(“hello world”));

}

结果就变成下面这样子

 

int main()

{

printf(“%s:%u:” “hello world” “\n”, __FUNCTION__, 9, );

}

这样最后就会多一个逗号,然后编译器报错。

 

这就不具有通用性,如果要解决这个问题就涉及到##,这个是宏中连接功能的符号,修改如下:

#define ILOG(fmt, …) \

“%s:%u:” fmt “\n”, \

__FUNCTION__, \

__LINE__, \

##__VA_ARGS__

 

int main()

{

printf(ILOG(“hello world”));

}

这样结果变为

 

int main()

{

printf(“%s:%u:” “hello world” “\n”, __FUNCTION__, 9);

}

 

就正确了,对于带format的情况,也是正确的。

 

二.Va_start, va_end,va_arg

 

当函数参数为可变参数时,可变部分表现在参数上就是…,那这部分参数如何解析一般就要用到va_start,va_end,va_arg

 

如void log(cosnt char *format, …)

{

    va_list args;

 

    va_start(args, format);

    、、、

    va_end(args);

}

 

一般的用法就如上,可变参数部分可以通过va_start转化,va_start中两个参数,第一个参数是一个va_list变量,用来存储可变参数首地址,第二个参数是最后一个确定参数。然后c中提供了许多基于va_list的处理函数,一般可以直接调用,比如vsnprintf等函数。

最后只需要调用一下va_end释放资源就好了。

 

这些函数主要在log中使用较多,基本只需要了解怎么用就可以了,没必要太深入的了解。有特殊需要时可以深入研究下。

 

说一下vsnprintf这个函数,这个函数还是蛮有用的,

len = vsnprintf(char *str, buf_len, const char*format, va_list args);

 

这个函数是把边长参数字符串按format格式化。

其中的注意点:1.如果格式化后的字符串长度小于等于buf_len – 1,那就代表是成功的,格式化后的字符串被存入str开始的buf_len – 1个Byte中,最后一个空间我们可以插入一个结束字符\0。 如果格式化后的字符串长度超过了buf_len – 1,则只写入buf_len – 1 个字符,此时返回的len为真是格式化字符串的长度,而不是写入的字符长度,此时len >= buf_len。

所以,我们可以先用该函数得到真实的格式化后字符串长度。如果返回值大于传入的buf_len,则说明我们当前预留的空间是不够的,需要realloc增加len – buf_len个字符空间。调整后,我们可以重新调用该函数来真正写入最终结果。

这个函数非常有用,在实现动态字符串时,允许接受边长格式化字符串时,该函数的功能就体现出来了。

例子如下

static void sds_put_va_args(struct sds *sds, const char *format, va_list args)

{

va_list args_;

size_t avail_len;

int need_len;

 

avail_len = sds->str ? sds->all – sds->len + 1 : 0;

va_copy(args_, args);

/* If the output was

truncated due to this limit then the return value is the number of

characters (excluding the terminating null byte) which would have been

written to the final string if enough space had been available. Thus,

a return value of size or more means that the output was truncated.

print at most avail_len – 1 characters*/

need_len = vsnprintf(sds->str + sds->len, avail_len, format, args_);

va_end(args_);

 

/* the buff is not enough for format,args + ‘\0’ */

if (need_len >= avail_len) {

sds_expand(sds, need_len);

avail_len = sds->all – sds->len + 1;

need_len = vsnprintf(sds->str + sds->len, avail_len, format, args);

}

sds->len += need_len;

}

本文遵从CC3.0协议转载请注明:转自凌风技术站

本文标题:开发日志模块时__VA_ARGS__怎么用va_start,va_end,va_copy用法说明

本文链接地址:https://www.iaccepted.net/tools/219.html

相关文章