我应该如何规避-Wformat-truncation?

时间:2019-03-22 09:55:28

标签: printf compiler-warnings truncation format-string gcc8

假设我有一个使用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);

我应该如何规避此警告?

2 个答案:

答案 0 :(得分:0)

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

  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);
    

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

答案 1 :(得分: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