如何检测`snprintf`错误? [英] How to detect `snprintf` errors?

查看:139
本文介绍了如何检测`snprintf`错误?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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_tunsigned 转换.

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-compareintsize_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 had n 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 than n. 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屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆