C编程-可变参数检查

Posted by 周思进 on January 27, 2021

在 C 语言编程中,有时候需要提供一些支持参数可变的接口,如自定义的日志记录接口,为方便外部调用,最终要记录的日志可以在接口内部进行组装,而不用外部先行组装好,类似于 printf 实现,内部直接借助 vsnprintf 接口将多个参数拼接成字符串,代码如下:

#include <stdarg.h>

void write_log(char *format, ...)
{
    va_list arg;
    char log_buf[1024] = {0};

    va_start(arg, format);
    vsnprintf(log_buf, sizeof(log_buf), format, arg);
    va_end(arg);

    ...

}

但如果只是这样实现那是有安全问题的,如调用方像下面这样传递了错误的参数,编译并不会报错,但运行时就崩溃了。

int main(int argc, char const *argv[])
{
    write_log("name:%s, arg:%d\n", 465, 18);
    return 0;
}

image

但如果你是用 printf 接口这样调用,编译就直接报错了,如下:

image

这是为什么呢? 通过查看 printf 函数声明,如下:

int printf (const char *__restrict, ...)
                _ATTRIBUTE ((__format__ (__printf__, 1, 2)));

其有个 attribute format 属性设置,该属性设置后,可用于检测从第2个参数开始,其类型是否和第1个格式化输出完全匹配,如果不匹配,就会编译报错。

所以类似实现上面的代码,都应该加上这样的属性设置,避免调用错误而发生异常情况。

上述例子则应该在对外提供的头文件中,对 write_log 接口增加属性设置

void write_log(char *format, ...) __attribute__((__format__(__printf__, 1, 2)));