c - 如何阻止 GCC 在 snprintf() 调用中提示 "directive output may be truncated"?

标签 c gcc

我在古老的 Linux (RedHat 5.2) 和现代的 macOS 10.14.6 Mojave 上都使用 GCC 9.2.0,我对两者都收到了同样的提示。

#include <stdio.h>
#include <time.h>

struct Example
{
    /* ... */
    char mm_yyyy[8];    /* Can't be changed */
    /* ... */
};

extern void function(struct tm *tm, struct Example *ex);

void function(struct tm *tm, struct Example *ex)
{
    snprintf(ex->mm_yyyy, sizeof(ex->mm_yyyy), "%d-%d",
             tm->tm_mon + 1, tm->tm_year + 1900);
}

使用 -Wall 编译时和任何优化(所以不是 -O0 并且根本没有优化选项),编译器说:
$ gcc -O -Wall -c so-code.c 
so-code.c: In function ‘function’:
so-code.c:15:49: warning: ‘%d’ directive output may be truncated writing between 1 and 11 bytes into a region of size 8 [-Wformat-truncation=]
   15 |     snprintf(ex->mm_yyyy, sizeof(ex->mm_yyyy), "%d-%d",
      |                                                 ^~
so-code.c:15:48: note: directive argument in the range [-2147483647, 2147483647]
   15 |     snprintf(ex->mm_yyyy, sizeof(ex->mm_yyyy), "%d-%d",
      |                                                ^~~~~~~
so-code.c:15:48: note: directive argument in the range [-2147481748, 2147483647]
so-code.c:15:5: note: ‘snprintf’ output between 4 and 24 bytes into a destination of size 8
   15 |     snprintf(ex->mm_yyyy, sizeof(ex->mm_yyyy), "%d-%d",
      |     ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
   16 |              tm->tm_mon + 1, tm->tm_year + 1900);
      |              ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
$

一方面,足够公平;如果 tm->tm_mon包含 0 到 99(或 -1 到 -9)范围之外的值,那么两个以上的字节将被写入输出缓冲区,或者如果 tm->tm_year + 1900需要超过 4 位数字,那么就会出现截断/溢出。但是,已知时间值是有效的(月 011 ;年 + 1900 在范围 19702100 中,为了具体起见;年的范围实际上更小 - 2019 .. 2025 年左右),因此实际上没有必要担心。

有没有办法在不诉诸代码的情况下抑制警告,例如:
#ifdef __GNUC__
#pragma GCC diagnostic push
#pragma GCC diagnostic ignore "-Wformat-overflow" /* Or "-Wformat-truncation" */
#endif

    snprintf(ex->mm_yyyy, sizeof(ex->mm_yyyy), "%d-%d",
             tm->tm_mon + 1, tm->tm_year + 1900);

#ifdef __GNUC__
#pragma GCC diagnostic pop
#endif
#ifdef行是必要的,因为代码必须由其他编译器(特别是 AIX 上的 XLC 13.x)编译,这些编译器会提示未知的编译指示,即使它们并不真正应该这样做。 (更正式地说,他们不需要提示并且应该接受忽略未知编译指示的代码,但是他们传递关于不识别编译指示的注释,这违背了干净编译的目标。)

只是为了踢;如果你改变函数返回 void返回 int ,如果你然后 return snprintf(…); ,不会产生错误。 (这让我感到惊讶 - 我不确定我是否理解为什么这也不是问题。我想这与'返回 snprintf() 的返回值有关,因此可以检查它并发现溢出,因此,但是这有点令人惊讶。)

不幸的是,这是一个 MCVE ( Minimal, Complete, Verifiable Example ;它从中提取的代码要大得多,而且改变数据结构不是一种选择——这只是它出现的函数中的一个步骤。

我想我可以写一个微观函数来调用 snprintf()并返回值(将被忽略),但是:
  • 有没有其他可行的替代方案?
  • 有没有办法告诉 GCC 传递给 snprintf() 的变量范围等人比最坏的情况更安全?
  • 最佳答案

    对于检查可能值范围的编译器,使用 %以快速限制范围。% some_unsigned_N确保输出在 [0... N-1] .
    请注意 % some_pos_int_N输出在 (-N ...N)范围所以推荐无符号数学以避免 '-'标志。

    snprintf(ex->mm_yyyy, sizeof(ex->mm_yyyy), "%d-%d", 
        //  tm->tm_mon + 1, tm->tm_year + 1900);
        (tm->tm_mon + 1)%100u, (tm->tm_year + 1900)%10000u);
    
    可能还想用"%u"应该some_unsigned_N附近INT_MAX .

    关于c - 如何阻止 GCC 在 snprintf() 调用中提示 "directive output may be truncated"?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/57780368/

    相关文章:

    c - 在函数参数中使用 union

    c++ - 如何消除仅在启用优化时出现的错误

    c - 对于使用标准功能的程序,我什么时候需要关心 C 中的静态链接与动态链接?

    c++ - std::async 不并行化任务

    c++ - gcc vector 扩展中的未对齐加载/存储

    c++ - 从可变参数模板中扩展的 decltype 继承

    c - 为什么这段代码不能正确地对包含数字的行进行排序?

    c - Ruby 垃圾回收 : Mark non-exported variables

    c - 在c编程中将值保存在内存中

    c - 检查读写系统调用的返回值的最佳方法是什么?