如何在 C 中定义一个接受格式化输入字符串的函数?

问题描述

我构建了一个自定义日志记录功能,它接收“日志级别”和字符串。用户将指定与消息关联的日志级别(即错误、警告、跟踪等)。日志功能只会根据当前配置的日志级别将消息打印到控制台。

int global_log_level = INFO;

void logger(int msg_log_level,char * msg)
{
    if(msg_log_level >= global_log_level )
    {
        printf("Log Level = %u: %s",msg_log_level,msg);
    }
}

理想情况下,我想将格式化的字符串提供给这个函数

logger(LOG_LEVEL_ERROR,"Error of %u occurred\n",error_code);

但是,通过添加此“包装”逻辑,我无法输入格式化的消息。相反,我必须将消息写入一个临时字符串,然后将其输入到函数中。

char temp[512];
sprintf("Error of %u occurred\n",error_code);
logger(LOG_LEVEL_ERROR,temp);

有没有办法实现记录器功能,这样我就不需要让用户自己创建临时字符串?

解决方法

这是 question 15.5 上的 C FAQ list

您需要 vprintf,它允许您编写自己的 printf 类函数 logger,但您可以在其中将实际格式字符串和参数传递给 vprintf 以做这项工作。诀窍是您需要构造一个特殊的 va_arg 类型来“指向”可变长度参数列表。它看起来像这样:

#include <stdarg.h>

int global_log_level = INFO;

void logger(int msg_log_level,char * msg,...)
{
    va_list argp;
    va_start(argp,msg);

    if(msg_log_level >= global_log_level )
    {
       printf("Log Level = %u: ",msg_log_level);
       vprintf(msg,argp);
    }

    va_end(argp);
}
,

作为对@SteveSummit 回答的补充,许多编译器都有特殊的编译指示或属性来表明该函数是“类似printf”的。它允许编译器在编译时检查参数,就像使用 printf

时所做的一样

gcc 示例:

format (archetype,string-index,first-to-check)

format 属性指定函数采用 printf、scanf、 应该进行类型检查的 strftime 或 strfmon 样式参数 针对格式字符串。例如声明:

              extern int
              my_printf (void *my_object,const char *my_format,...)
                    __attribute__ ((format (printf,2,3)));
              

使编译器检查对 my_printf 的调用中的参数与 printf 样式格式字符串参数的一致性 我的格式。 参数 archetype 决定如何解释格式字符串,应该是 printf、scanf、strftime 或 strfmon。 (你 也可以使用 __printf____scanf____strftime____strfmon__。) 参数 string-index 指定哪个参数是格式字符串 参数(从 1 开始),而 first-to-check 是 检查格式字符串的第一个参数。对于函数,其中 无法检查参数(例如 vprintf), 将第三个参数指定为零。在这种情况下,编译器只 检查格式字符串的一致性。对于 strftime 格式, 第三个参数必须为零。

在上面的例子中,格式字符串(my_format)是函数 my_print 的第二个参数,以及检查开始的参数 使用第三个参数,因此格式的正确参数 属性为 2 和 3。

format 属性允许你识别你自己的以格式字符串作为参数的函数,以便 GCC 可以检查 调用这些函数以查找错误。编译器总是(除非 -ffreestanding 被使用)检查标准库函数 printf、fprintf、sprintf、scanf、fscanf、sscanf、strftime、 vprintf、vfprintf 和 vsprintf 每当请求此类警告时 (使用-Wformat),所以不需要修改头文件 stdio.h。在 C99 模式下,函数 snprintf、vsnprintf、vscanf、 vfscanf 和 vsscanf 也被检查。除了严格遵守 C 标准模式,X/Open 函数 strfmon 也按原样检查 printf_unlocked 和 fprintf_unlocked。请参阅控制 C 的选项 方言。

来源:https://gcc.gnu.org/onlinedocs/gcc-3.2/gcc/Function-Attributes.html