printf:混合 va_list 和可变参数

问题描述 投票:0回答:1

我有一个日志记录功能,它是按照以下几行实现的。基本思想是我想为用户提供类似 printf 的格式,但要在记录的字符串前加上时间戳并添加 ' ' 最后:

void write_log(bb_log_level_t level, const char *fmt, ...) {
    if (level >= current_log_level) {
        // prefix with timestamp and log level name
        printf("%20s [%s]: ", get_timestamp(), log_level_name(level));
        // print user's message
        va_list ap;
        va_start(ap, fmt);
        vprintf(fmt, ap);
        va_end(ap);
        // end with newline
        printf("\n");
}

这行得通,但我想知道是否有办法将这一切整合到一个

vprintf
电话中。

我猜答案是否定的,因为没有用于修改 va_list 的已发布接口,并且扩展 fmt 字符串比简单地调用 printf / vprintf / printf 需要更多的工作。但问问也没什么坏处……

c printf variadic-functions
1个回答
1
投票

不,您不能在标准 C 中扩展此函数内部提供的

va_list
(在调用之后)。也许您可以在调用函数之前创建一个扩展
va_list
和格式字符串的宏。

原因是,

va_list
被放置在堆栈上(至少在大多数实现中),一些(大多数/所有?)实现在其后放置额外的数据(返回地址,局部变量,...)。您想扩展该列表,但它后面可能没有空格,因为它已被使用,所以没有复杂的汇编程序黑客就无法扩展它。

好吧,你可以不在

(v)printf()
中做数据的序列化,而是自己做,但为什么要那样做呢?您也可以使用
vsnprintf()
,但这也没有用。它会将对 `(v)printf() 的调用减少到 1 次调用,但您不会从中获得任何好处。

像这样:

char buffer[1024];
if( vsnprintf(buffer,1024,fmt,ap)<0 )
  { va_end(ap); return -1; }
printf("%20s [%s]: %s\n", get_timestamp(), log_level_name(level), buffer);

注意:当输出长于

'\0'
时,不确定
buffer
是否包含在
1024
中。你必须检查一下。您还必须担心最大输出长度,
1024
够吗?

© www.soinside.com 2019 - 2024. All rights reserved.