我在古老的 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 位数字,那么就会出现截断/溢出。但是,已知时间值是有效的(月 0
到 11
;年 + 1900 在范围 1970
到 2100
中,为了具体起见;年的范围实际上更小 - 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()
并返回值(将被忽略),但是: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/