如何检测`snprintf`错误? [英] How to detect `snprintf` errors?
问题描述
int snprintf(char * 限制 s, size_t n, const char * 限制格式, ...);
snprintf()
很好地防止了目标 s
溢出.然而,当目的地不足以获得完整的结果时,如何检测该错误和其他错误?
以下是否足够?.
char buf[11 + 10 + 1];if (snprintf(buf, sizeof buf, "Random int %d", rand()) >= sizeof buf) {fprintf(stderr, "缓冲区太小");//也许 `int` 是 64 位的?退出(EXIT_FAILURE);}
一>.欢迎提供其他答案.
<块引用>如何检测 C 中的 snprintf
错误?
简答
回忆 snprintf()
返回一个 int
.
使用更广泛的 size_t
或 unsigned
转换.
if ((size_t) snprintf(... ) >= sizeof buf) {错误();}
或迂腐
int length_needed = snprintf(...);if (length_needed < 0 || (unsigned) length_needed >= sizeof buf) {错误();}
截断测试
有时从 snprintf()
中检测截断的字符串是非常重要的.
char buf[13];char *command =format_drive";char *sub_command =取消";snprintf(buf, sizeof buf, %s %s", command, sub_command);系统(缓冲);//system("format_drive") 导致再见数据
OK 代码
一次测试返回值是否满足或超过目标数组的大小几乎就足够了.
char buf[20];如果(snprintf(buf,sizeof buf,随机整数%d",rand())> = sizeof buf){fprintf(stderr, 缓冲区太小");退出(EXIT_FAILURE);}
负值
<块引用>snprintf
函数返回在 n
足够大的情况下会写入的字符数,不计算终止空字符,或者如果编码是负值错误发生.因此,当且仅当返回值为非负且小于 n
时,以 null 结尾的输出才被完全写入.C11dr §7.21.6.5 3
健壮的代码会直接检查罕见的编码错误的负值.if (some_int <= some_size_t)
不幸的是还不够,因为 int
将被转换为 size_t
.int
负返回值然后变成一个很大的正 size_t
.这通常远大于数组的大小,但未指定为如此.
//迂回检查负值int length_needed = snprintf(... 同上...);if (length_needed < 0 || length_needed >= sizeof buf) {fprintf(stderr,缓冲区太小(或编码错误)");退出(EXIT_FAILURE);}
不匹配符号
一些编译器警告抱怨比较不同符号的整数,例如 gcc 的 -Wsign-compare
与 int
和 size_t
.强制转换为 size_t
似乎是合理的.
警告:有符号和无符号整数表达式之间的比较[-Wsign-compare]
//安静的不同符号警告int length_needed = snprintf(... 同上...);if (length_needed < 0 || (size_t) length_needed >= sizeof buf) {fprintf(stderr,缓冲区太小(或编码错误)");退出(EXIT_FAILURE);}
迂腐
C 没有指定 int
的正值是 size_t
的子范围.size_t
可以是 unsigned short
然后 SIZE_MAX
INT_MAX
.(我知道没有这样的实现.)因此对 (size_t) some_int
的强制转换可能会改变该值.相反,将正返回值转换为 unsigned
(INT_MAX <= UINT_MAX
始终为真)不会改变该值,并且将确保使用最宽的无符号类型进行比较unsigned
和 size_t
之间.
//安静的不同符号警告int length_needed = snprintf(... 同上...);if (length_needed < 0 || (unsigned) length_needed >= sizeof buf) {fprintf(stderr,缓冲区太小(或编码错误)");退出(EXIT_FAILURE);}
int snprintf(char * restrict s, size_t n, const char * restrict format, ...);
snprintf()
nicely prevents overrunning the destination s
. Yet when the destination is insufficient for the complete result, how to detect that and other errors?
Is the following sufficient?.
char buf[11 + 10 + 1];
if (snprintf(buf, sizeof buf, "Random int %d", rand()) >= sizeof buf) {
fprintf(stderr, "Buffer too small"); // Maybe `int` was 64-bit?
exit (EXIT_FAILURE);
}
This is part of Can I answer my own question?. Additional answers are welcome.
How to detect
snprintf
errors in C?
Short answer
Recall snprintf()
returns an int
.
Use the wider of size_t
or unsigned
cast.
if ((size_t) snprintf(... ) >= sizeof buf) {
error();
}
or pedantically
int length_needed = snprintf(... );
if (length_needed < 0 || (unsigned) length_needed >= sizeof buf) {
error();
}
Testing for truncation
Sometimes it is very important to detect a truncated string from 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 code
A single test if the return value meets or exceeds the size of the destination array is nearly sufficient.
char buf[20];
if (snprintf(buf, sizeof buf, "Random int %d", rand()) >= sizeof buf) {
fprintf(stderr, "Buffer too small");
exit(EXIT_FAILURE);
}
Negative value
The
snprintf
function returns the number of characters that would have been written hadn
been sufficiently large, not counting the terminating null character, or a negative value if an encoding error occurred. Thus, the null-terminated output has been completely written if and only if the returned value is nonnegative and less thann
. C11dr §7.21.6.5 3
Robust code would directly check for a negative value for the rare encoding error. if (some_int <= some_size_t)
unfortunately is not sufficient as the int
will be converted to a size_t
. An int
negative return value then becomes a large positive size_t
. This usually is far larger than the size of the array yet is not specified to be so.
// Pedantic check for negative values
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);
}
Mis-match sign-ness
Some compiler warnings whine about comparing integers of different sign-ness such as gcc's -Wsign-compare
with int
and size_t
. Casting to size_t
seems reasonable.
warning: comparison between signed and unsigned integer expressions [-Wsign-compare]
// Quiet different sign-ness warnings
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);
}
Pedantic
C does not specify that the positive values of int
are a sub-range of size_t
. size_t
could be unsigned short
and then SIZE_MAX < INT_MAX
. (I know of no such implementation.) Thus a cast to (size_t) some_int
could alter the value. Instead, casting the positive return value to unsigned
(INT_MAX <= UINT_MAX
is always true) will not alter the value and will ensure the compare is done with the widest unsigned type between unsigned
and size_t
.
// Quiet different sign-ness warnings
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);
}
这篇关于如何检测`snprintf`错误?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!