GCC中-Wformat-truncation警告含义及非消除式解决方法
问题
代码
#include<stdio.h> #include<stdlib.h> #include<string.h> #include<time.h> int main(int argc, char **argv){ time_t curr_time; curr_time = time(NULL); struct tm tm = *localtime(&curr_time); char file_name[41]; snprintf(file_name, sizeof(file_name), "backup_%02d.%02d.%02d-%02d.%02d.%02d.tar.lz4.gpg", tm.tm_mday, tm.tm_mon+1, tm.tm_year+1900, tm.tm_hour, tm.tm_min, tm.tm_sec); printf("%s", file_name); return 0; }
编译警告
执行命令gcc -Wall -O3 ./gen.c时出现以下警告:
./gen.c: In function ‘main’: ./gen.c:11:77: warning: ‘%02d’ directive output may be truncated writing between 2 and 11 bytes into a region of size between 0 and 18 [-Wformat-truncation=] 11 | snprintf(file_name, sizeof(file_name), "backup_%02d.%02d.%02d-%02d.%02d.%02d.tar.lz4.gpg", tm.tm_mday, tm.tm_mon+1, tm.tm_year+1900, tm.tm_hour, tm.tm_min, tm.tm_sec); | ^~~~ In file included from /usr/include/stdio.h:894, from ./gen.c:1: /usr/include/x86_64-linux-gnu/bits/stdio2.h:71:10: note: ‘__builtin___snprintf_chk’ output between 37 and 91 bytes into a destination of size 40 71 | return __builtin___snprintf_chk (__s, __n, __USE_FORTIFY_LEVEL - 1, | ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 72 | __glibc_objsize (__s), __fmt, | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 73 | __va_arg_pack ()); | ~~~~~~~~~~~~~~~~~
疑问
- 该警告的含义是什么?
- 如何在不消除该警告的前提下解决此问题?
解答
警告含义
这个-Wformat-truncation=是GCC的安全警告,触发原因是:
- GCC基于类型做最坏情况分析:格式串里的
%02d对应int类型参数,理论上最多能输出11字节(比如32位int的最大值2147483647是10位数字加符号),叠加格式串的固定部分后,总长度可能达到91字节,远大于你定义的40字节缓冲区。 - 实际运行时,
tm结构体的字段有明确取值范围:日期1-31、月份1-12、年份1900至今(4位)、时分秒0-59(2位),格式化后的字符串实际最多37字节(加终止符\0是38),完全能放进40字节缓冲区,但GCC不考虑实际业务逻辑,只做类型层面的风险提示,所以抛出了这个警告。
不消除警告的解决方法
核心是在运行时确保不会出现截断,同时保留GCC的警告提示:
增加运行时长度校验
调用snprintf后,检查它的返回值:返回值代表格式化后字符串的总长度(不含终止符),如果这个值大于等于缓冲区大小,说明发生了截断,此时直接报错退出,避免使用不完整的文件名:
int ret = snprintf(file_name, sizeof(file_name), "backup_%02d.%02d.%02d-%02d.%02d.%02d.tar.lz4.gpg", tm.tm_mday, tm.tm_mon+1, tm.tm_year+1900, tm.tm_hour, tm.tm_min, tm.tm_sec); if (ret >= sizeof(file_name)) { fprintf(stderr, "生成的文件名过长,超出缓冲区限制\n"); return EXIT_FAILURE; }
这样即使GCC警告,代码也能在实际出现异常时及时终止,避免错误扩散。
内容的提问来源于stack exchange,提问作者Trevor Philip
相关产品推荐
相关产品推荐

