使用 gcc 版本 13.2.0 (Ubuntu 13.2.0-23ubuntu4) 以下代码编译正常:
char mm[3]; sprintf (mm, "%02d", date_struct->tm_mon); // tm_mon is from 0 to 11
gcc 如何知道 tm_mon 适合 2 位数字?它知道范围限制 0..11 吗?
以下代码会引发警告:
char mm[3]; sprintf (mm, "%02d", date_struct->tm_mon+1);
将 2 到 11 字节的指令写入大小为 3 的区域
gcc 会“忘记”范围吗?
gcc 会“忘记”范围吗?
是的。
令我惊讶的是
gcc
知道 tm_mon
的范围,但毫不奇怪,一旦 tm_mon
变成表达式,它就会失去该范围。
理论上,
gcc
可以在编译时继续跟踪此类表达式,但最终这相当于停止问题,因此在遇到第一个+
(或-
)时立即停止对我来说似乎是合理的。
令人惊讶的是,使用
1000 * date_struct->tm_mon
不会 导致警告(但应该)。所以整个警告似乎都是半生不熟。
#include <stdio.h>
#include <time.h>
#ifndef XXX
#define XXX 0
#endif
void fn(struct tm *date_struct) {
char mm[3]; sprintf (mm, "%02d", date_struct->tm_mon + XXX);
}
gcc --version
gcc (GCC) 14.2.1 20240801 (Red Hat 14.2.1-1)
...
gcc -c -Wall tt.c
# no output
gcc -c -Wall tt.c -DXXX=1
tt.c:9:29: warning: ‘%02d’ directive writing between 2 and 11 bytes into a region of size 3 [-Wformat-overflow=]
9 | char mm[3]; sprintf (mm, "%02d", date_struct->tm_mon + XXX);
| ^~~~
tt.c:9:28: note: directive argument in the range [-2147483647, 2147483647]
9 | char mm[3]; sprintf (mm, "%02d", date_struct->tm_mon + XXX);
| ^~~~~~
tt.c:9:15: note: ‘sprintf’ output between 3 and 12 bytes into a destination of size 3
9 | char mm[3]; sprintf (mm, "%02d", date_struct->tm_mon + XXX);
| ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~