这是我可以回答自己的问题吗?的一部分。欢迎提出其他答案。
如何在C语言中检测snprintf
错误?
简短回答
记得snprintf()
返回一个int
用于指示将要写入的字符串长度或负值表示编码错误。
if ((size_t) snprintf(... ) >= sizeof buf) {
error();
}
严谨地说,使用更宽的
size_t
或
unsigned
转换,所以最好是:
int length_needed = snprintf(... );
if (length_needed < 0 || (unsigned) length_needed >= sizeof buf) {
error();
}
测试截断
有时候从snprintf()
检测到截断的字符串非常重要。缺乏测试可能会导致麻烦:
char buf[13];
char *command = "format_drive";
char *sub_command = "cancel";
snprintf(buf, sizeof buf, "%s %s", command, sub_command);
system(buf); // system("format_drive") leads to bye-bye data
OK 代码
只需单独测试返回值是否符合或超过目标数组的大小几乎足够了。
char buf[20];
if (snprintf(buf, sizeof buf, "Random int %d", rand()) >= sizeof buf) {
fprintf(stderr, "Buffer too small");
exit(EXIT_FAILURE);
}
负值
snprintf
函数返回将被写入的字符数,如果n
足够大,则不计算终止的空字符,或者如果发生编码错误则返回负值。因此,只有当返回值为非负且小于n
时,才完全写入了以空字符结尾的输出。C11dr §7.21.6.5 3
健壮的代码应直接检查是否存在罕见的编码错误的负值。不幸的是,if (some_int <= some_size_t)
并不足够,因为int
会被转换为size_t
。然后,一个int
的负返回值就变成了一个很大的正size_t
。这通常远大于数组的大小,但没有规定必须如此。
int length_needed = snprintf(... as above ...);
if (length_needed < 0 || length_needed >= sizeof buf) {
fprintf(stderr, "Buffer too small (or encoding error)");
exit(EXIT_FAILURE);
}
类型不匹配
一些编译器警告会抱怨比较不同类型的整数,例如gcc的-Wsign-compare
与int
和size_t
。将其转换为size_t
似乎是合理的。
警告:有符号和无符号整数表达式之间的比较[-Wsign-compare]
int length_needed = snprintf(... as above ...);
if (length_needed < 0 || (size_t) length_needed >= sizeof buf) {
fprintf(stderr, "Buffer too small (or encoding error)");
exit(EXIT_FAILURE);
}
追求严谨
C语言没有规定int
类型的正值是size_t
类型的子范围。 size_t
可以是unsigned short
,然后SIZE_MAX < INT_MAX
。(我不知道是否有这样的实现。)因此,将some_int
强制转换为(size_t)
可能会改变其值。相反,将正返回值强制转换为unsigned
(INT_MAX <= UINT_MAX
始终成立)将不会改变其值,并确保使用unsigned
和size_t
之间最宽的无符号类型进行比较。
int length_needed = snprintf(... as above ...);
if (length_needed < 0 || (unsigned) length_needed >= sizeof buf) {
fprintf(stderr, "Buffer too small (or encoding error)");
exit(EXIT_FAILURE);
}
asprintf
来彻底解决此类问题,它会在堆上分配字符串。您仍然需要检查它是否有效(底层的malloc
可能会失败),但您无需担心缓冲区不足的问题。 缺点:您需要释放构建的字符串,另外需要时间来分配和释放空间。 - Peretteasprintf()
不是标准的C库函数,尽管它经常出现在各种扩展库中。 - chux - Reinstate MonicaGCC -O1
标志也会显示错误。如果未指定,则不会出现错误。 - jian