- 该警告在gcc7.1中添加,详见gcc7.1发布变更。
- 从gcc文档中:
-Wformat-truncation的级别1[...]只会对返回值未使用且很可能导致输出截断的有界函数调用发出警告。
- 该问题是一个错误报告,并被标记为NOTABUG:
未处理的输出截断通常是程序中的错误。[...]
在截断被期望的情况下,调用者通常会检查从函数返回的值并以某种方式进行处理(例如,通过分支)。 在这些情况下,不会发出警告。 警告打印的源行表明这不是这些情况之一。 该警告正在执行其设计的功能。
- 但我们可以仅检查snprintf的返回值,该函数在错误时返回负值。
#include <stdio.h>
#include <stdlib.h>
int main() {
char dst[2], src[2] = "a";
int ret = snprintf(dst, sizeof(dst), "%s!", src);
if (ret < 0) {
abort();
}
for (int ret = snprintf(dst, sizeof(dst), "%s!", src); ret < 0;) exit(ret);
snprintf(dst, sizeof(dst), "%s!", src) < 0 ? abort() : (void)0;
#define snprintf_nowarn(...) (snprintf(__VA_ARGS__) < 0 ? abort() : (void)0)
snprintf_nowarn(dst, sizeof(dst), "%s!", src);
}
在
https://godbolt.org/上使用gcc7.1、gcc7.2、gcc7.3和gcc8.1进行测试,使用
-O{0,1,2,3} -Wall -Wextra -pedantic
编译选项,没有警告。使用大于
-O1
的优化时,gcc8.1会优化/删除对
abort()
的调用。
奇怪的是,当编译为C++源文件时,即使检查返回值,警告仍然存在。在C中一切正常。在C++中最好使用std::format_to
。所以:
- 我们可以使用特定于编译器的语法来禁用警告。
#include <stdio.h>
#include <stdlib.h>
int main() {
char dst[2];
char src[2] = "a";
int ret = snprintf(dst, sizeof(dst), "%s!", src);
if (ret < 0) {
abort();
}
ret = snprintf(dst, sizeof(dst), "%s!", "a");
if (ret < 0) {
abort();
}
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wformat-truncation"
snprintf(dst, sizeof(dst), "%s!", "a");
#pragma GCC diagnostic pop
#ifndef __GNUC__
#define snprintf_nowarn snprintf
#else
#define snprintf_nowarn(...) __extension__({ \
_Pragma("GCC diagnostic push"); \
_Pragma("GCC diagnostic ignored \"-Wformat-truncation\""); \
const int _snprintf_nowarn = snprintf(__VA_ARGS__); \
_Pragma("GCC diagnostic pop"); \
_snprintf_nowarn; \
})
#endif
snprintf_nowarn(dst, sizeof(dst), "%s!", "a");
}
ret = snprintf()
调用后跟着if (ret < 0)
,我们将不会收到任何警告,这是错误的!在截断时,ret 将为正数。增加目标数组并不能解决任何问题。GCC 不关心它,它只验证返回值是否被检查,但从我的角度来看,这样做很糟糕。 - fralbo