1

假设我有一个函数int *p,我知道它只指向 0 到 99 之间的值。但是,编译器不知道,所以如果我写:

char buffer[3];
snprintf(buffer, "%02d", *p);

我收到一个警告(至少在 GCC 8.x 上) - 类似于:

warning: ‘%02d’ directive output may be truncated writing between 2 and 11 bytes into a region of size 2 [-Wformat-truncation=]
   snprintf(buffer, "%02d", *p);

我应该如何规避这个警告?

4

2 回答 2

2

我可以想到三种避免警告的方法:

  1. 使用 GCC 编译指示进行本地抑制:

    #if __GNUC__ >= 8
    #pragma GCC diagnostic push
    #pragma GCC diagnostic ignored "-Wformat-truncation"
    #endif
    snprintf(buffer, "%02d", *p);
    
    #if __GNUC__ >= 8
    #pragma GCC diagnostic pop
    #endif
    
  2. 无用地钳制打印值以使编译器知道范围:

    char buffer[3]; 
    int clamped_value = min(max(*p,0),99)` and print that instead of `*p`.
    snprintf(buffer, "%02d", clamped_value);
    
  3. 人为地将缓冲区大小增加了 9 个字节;

    char buffer[3+9]; 
    snprintf(buffer, "%02d", p);
    

但我不太喜欢这些。第一种方法不太安全(而且更冗长);第二个浪费时钟周期,第三个浪费堆栈空间。

于 2019-03-22T09:55:28.360 回答
0

这段代码在 GCC 8.3.1 上编译并运行良好:

#include <stdio.h>

#define BUF_SZ 3

int main(void)
{
    int foo = 99;
    int *p = (int *)&foo;
    char buffer[BUF_SZ];

    snprintf(buffer, BUF_SZ, "%02d", *p);

    fprintf(stdout, "%s\n", buffer);

    return 0;
}
me@localhost:/tmp$ gcc -v 2>&1 | grep "gcc version"
gcc version 8.3.1 20190223 (Red Hat 8.3.1-2) (GCC)

me@localhost:/tmp$ gcc -Wall test.c && ./a.out 
99

也许这个版本的 GCC 没有问题,但我确实在上面注意到你有 snprintf(buffer, "%02d", *p);- 因为你缺少 snprintf 的 size 参数,所以根本不应该编译。

此外,值得注意的是,对于以下差异,GCC 8.3.1 确实会按预期抛出错误:

me@localhost:/tmp$ diff test.c test_format-truncation-warning.c
11c11
<     snprintf(buffer, BUF_SZ, "%02d", *p);
---
>     snprintf(buffer, BUF_SZ, "%03d", *p);

对于偶然发现此页面的其他人,他们正在为 -Wformat-truncation 警告寻找更通用的“解决方法”。这是一个使用 memcpy() 的方法,尽管我怀疑 -Wformat-truncation 的作者打算将其用作 strncpy() 的替代方法。

#if USE_STRNCPY
    /* Note that using size of 'NAME_MAX' is just to prevent recent versions
     * of GCC from throwing '-Wformat-truncation' errors.  Otherwise, a char
     * buffer of len UUID_STR_LEN would be fine.
     */
    char tmp_fname[NAME_MAX + 1] = {0};
    strncpy(tmp_fname, input_file_name, NAME_MAX);
#else
    char tmp_fname[UUID_STR_LEN] = {0};
    memcpy((void *)tmp_fname, (void *)input_file_name,
           MIN(UUID_STR_LEN - 1, strnlen(input_file_name, UUID_STR_LEN - 1));
#endif
于 2020-01-30T17:58:52.230 回答