如何检测“ snprintf”错误?

时间:2019-01-08 02:42:52

标签: c printf

int snprintf(char * restrict s, size_t n, const char * restrict format, ...);

snprintf()可以很好地防止超出目标s。但是,当目的地不足以得到完整结果时,如何检测到该错误和其他错误?

以下条件是否足够?

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);
}

1 个答案:

答案 0 :(得分:8)

这是Can I answer my own question?的一部分。欢迎提供其他答案。

  

如何检测C中的snprintf错误?

使用size_tunsigned转换中的较大者。

if ((size_t) snprintf(... ) >= sizeof buf) {
  error();
}

或学步地

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

确定代码

一次测试返回值是否达到或超过目标数组的大小就足够了。几乎

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.53

严格的代码会直接检查负值,以解决罕见的编码错误。不幸的是,if (some_int <= some_size_t)是不够的,因为int将转换为size_tint的负返回值然后变为大的正size_t。通常,该 远大于数组的大小,但尚未指定。

// Pedantic check for negative values 
int length_needed = snprintf(... as above ...);
if (length_needed < 0 || length_needed >= sizeof buf) {
  fprintf(stderr, "Buffer too small");
  exit(EXIT_FAILURE);
}

符号不匹配

一些编译器警告抱怨将不同符号的整数(例如gcc的-Wsign-compareintsize_t进行比较。强制转换为size_t

  

警告:有符号和无符号整数表达式[-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");
  exit(EXIT_FAILURE);
}

pedantic

C没有指定int的正值是size_t的子范围。 size_t可以是unsigned short,然后是SIZE_MAX < INT_MAX。 (我知道没有这样的实现。)因此,对(size_t) some_int的强制转换可以更改该值。相反,将正返回值强制转换为unsignedINT_MAX <= UINT_MAX始终为true)不会更改该值,并且将确保使用unsigned和{{1之间的最大无符号类型进行比较。 }}。

size_t