错误地将字符串传递给printf样式的日志函数时丢失错误

时间:2016-04-04 16:25:50

标签: c++ gcc

对于任何中型到大型项目来说,用自定义日志功能替换printf是很常见的。这是一个最小的C ++示例及其用法:

#include <stdio.h>
#include <stdarg.h>
#include <string>

void log_printf(const char* fmt, ...) {
    va_list ap;
    va_start(ap, fmt);
    vprintf(fmt, ap); // real code obviously does something fancier
    va_end(ap);
}

int main() {
    std::string x = "Hello";

    // correct code
    printf("String is %s\n", x.c_str());
    log_printf("String is %s\n", x.c_str());

    // incorrect code
    printf("String is %s\n", x); // bad line 1
    log_printf("String is %s\n", x); // bad line 2
}

简单记录器接收可变数量的参数并调用vprintf将它们输出到标准输出。正确代码下面的行&#39;演示此记录器的正确用法。我的问题涉及到“坏”的问题。行,错误地传递字符串对象而不是指向字符缓冲区的指针。

在GCC 4.6下(在Linux下测试),坏线都不能编译,这是一件好事,因为我想抓住这种不正确的用法。错误是:

error: cannot pass objects of non-trivially-copyable type ‘std::string {aka struct std::basic_string<char>}’ through ‘...’

然而,在GCC 5.1中,显然可以传递非平凡的可复制对象,并且编译成功。如果我使用-Wall那么只有'坏线'1&#39;提出了一个关于意外参数类型的警告,但是第2行和第2行不好。在任何情况下,log_printf编译都没有问题。不用说两条线都会产生垃圾输出。

我可以抓住第1行和第3行。使用-Wall -Werror,但是第2行&#39;如何使它也产生编译错误?

1 个答案:

答案 0 :(得分:2)

For your own functions you need to use a common function attribute call format:

void log_printf(const char* fmt, ...) __attribute__((format (printf, 1, 2)));

void log_printf(const char* fmt, ...) {
    ...
}

Note that the attribute must be set on a function declaration, not the definition.

The first argument to the format attribute is the style, in this case printf (scanf is also possible, for functions that works like scanf), the second argument is the format string, and the third argument is where the ellipsis ... is. It will help GCC check the format strings like for the standard printf function.

This is a GCC extension, though some other compilers have adopted it to become GCC compatible, most notably the Intel C compiler ICC and Clang (the standard compiler used on OSX and some BSD variants). The Visual Studio compiler does not have this extension, and I know of no similar thing for Visual C++.