8

我正在编写的小 C 库中有一个错误报告功能。errorf除了普通函数之外,我还想提供一个函数,error以便轻松地将信息嵌入错误消息中。

/*
 * Prints a formatted error message. Use it as you would use 'printf'. See the
 * 'sio_error' function.
 */
void sio_errorf(const char *format, ...) {
    // Print the error prefix                           
    if (g_input == STDIN) fputs("error: ", stderr);
    else fprintf(stderr, "%s: ", g_progname);

    // Pass on the varargs on to 'vfprintf'. 
    va_list arglist;
    va_start(arglist, format);
    // This may produce the following warning -- ignore it:
    //     warning: format string is not a string literal
    vfprintf(stderr, format, arglist);
    va_end(arglist);
    fputc('\n', stderr);
}

问题是,我收到了这个警告(使用-Weverything开关编译 clang 4.0):

警告:格式字符串不是字符串文字

我明白为什么这样做会很糟糕。有什么办法可以摆脱这个警告吗?我可以以某种方式强制format参数sio_errorf是字符串文字,以便编译器知道它总是会这样,并且我只是将它传递下去吗?

我知道我可以使用-Wno-format-nonliteral,但只有当其他人也打算手动编译它时,他们才不会这样做。我宁愿在源代码中添加一些可以消除警告的内容。

sio_errorf 理想情况下,如果我传递给的字符串实际上不是文字,我仍然会收到警告,但我不确定这是否可能。

4

2 回答 2

13

如果您使用的是 GCC 或其亲属之一,请尝试声明中的属性:

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

要将属性添加到定义中,您可以使用以下命令:

__attribute__((format(printf, 1, 2)))
    static void sio_errorf(const char *format, ...) {
      ....
于 2012-08-29T05:07:04.487 回答
10

许多编译器允许您以一种或另一种方式设置警告级别。例如,gcc 允许-W在调用编译器时通过命令行上的标志进行控制。

希望这是您使用的编译器,因为这样的程序:

#include <stdio.h>
int main (void) {
    char *s = "xyzzy\n";
    printf (s);
    return 0;
}

生成您描述的确切消息(假设您已使用-Wformat和启用警告-Wformat-nonliteral)。

您要查找的特定命令行参数是:

-Wno-format-nonliteral

这将防止有关在这些函数中使用非文字字符串的投诉。

但是,您可能正在寻找更细粒度的东西,因此它还允许您使用 pragma在代码中动态指定某些诊断消息的处置:

#include <stdio.h>
#pragma GCC diagnostic ignored "-Wformat-nonliteral"
int main (void) {
    char *s = "xyzzy\n";
    printf (s);
    return 0;
}
#pragma GCC diagnostic warning "-Wformat-nonliteral"

如果你用 编译它-Wformat -Wformat-nonliteral,你不会看到警告,因为你已经告诉 gcc 忽略该main函数的特定警告。

比我运行的更高版本的 gcc 具有以下选项:

#pragma GCC diagnostic push
#pragma GCC diagnostic pop

这将推送和弹出诊断的状态。这解决了我上面代码中的问题,您可能会将警告配置为错误- 我的第二个编译指示会将其更改为警告。

使用 push/pop 将允许通过以下方式恢复其原始配置:

#include <stdio.h>
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wformat-nonliteral"
int main (void) {
    char *s = "xyzzy\n";
    printf (s);
    return 0;
}
#pragma GCC diagnostic pop
于 2012-08-29T05:11:21.000 回答