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

标签 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 将它们输出到标准输出。 “正确代码”下的行演示了此记录器的正确用法。我的问题涉及“坏”行,其中错误地传递了字符串对象而不是指向字符缓冲区的指针。

在 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,则只有“bad line 1”会引发有关意外参数类型的警告,但带有 log_printf 的“bad line 2”在任何情况下都可以编译而不会出现问题。不用说,两条线都会产生垃圾输出。

我可以使用 -Wall -Werror 捕获“坏线 1”,但是“坏线 2”呢?我怎样才能让它也产生编译错误?

最佳答案

对于您自己的功能,您需要使用 common function attribute调用格式:

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

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

请注意,该属性必须在函数声明上设置,而不是在定义上设置。

format 属性的第一个参数是样式,在本例中是 printf (scanf 也是可能的,对于像这样工作的函数scanf),第二个参数是格式字符串,第三个参数是省略号 ... 所在的位置。它将帮助 GCC 检查格式字符串,就像标准 printf 函数一样。

这是一个 GCC 扩展,尽管其他一些编译器已采用它来兼容 GCC,最著名的是 Intel C 编译器 ICC 和 Clang(OSX 和某些 BSD 变体上使用的标准编译器)。 Visual Studio 编译器没有这个扩展,据我所知,Visual C++ 也没有类似的东西。

关于c++ - 错误地将字符串传递给 printf 样式日志函数时丢失错误,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/36407973/

相关文章:

C++ 跳出 vector 循环

c++ - 在函数中分配内存并返回它

c++ - "call"看似跳入自身的指令

linux - GCC 在构建 libgcc 时依赖于 GNU 汇编器?

c - 为什么 GCC 内联汇编器需要破坏信息,而 MSVC 不需要

c++ - 遇到小于 n 的简单计数素数问题

c++ - 4维游戏渲染优化

c++ - 我还应该在 C++11 中返回 const 对象吗?

c - 如何编译使用 Lua 5.1 的 C API 的代码?

c - Flex 和 Bison 在使用结构体指针作为 union 类型时会出现问题