如何检测`snprintf`错误?

chu*_*ica 4 c printf

int snprintf(char * restrict s, size_t n, const char * restrict format, ...);
Run Code Online (Sandbox Code Playgroud)

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);
}
Run Code Online (Sandbox Code Playgroud)

chu*_*ica 9

这是我可以回答我自己的问题的一部分吗?.欢迎提供其他答案.

如何检测snprintfC中的错误?

使用更广泛size_tunsigned演员.

if ((size_t) snprintf(... ) >= sizeof buf) {
  error();
}
Run Code Online (Sandbox Code Playgroud)

或迂腐

int length_needed = snprintf(... );
if (length_needed < 0 || (unsigned) length_needed >= sizeof buf) {
  error();
}
Run Code Online (Sandbox Code Playgroud)

测试截断

有时从中检测截断的字符串非常重要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
Run Code Online (Sandbox Code Playgroud)

好的代码

如果返回值满足或超过目标数组的大小的单个测试是几乎足够.

char buf[20];
if (snprintf(buf, sizeof buf, "Random int %d", rand()) >= sizeof buf) {
  fprintf(stderr, "Buffer too small");
  exit(EXIT_FAILURE);
}
Run Code Online (Sandbox Code Playgroud)

负值

snprintf函数返回已写入的字符数n足够大,不计算终止空字符,如果发生编码错误则返回负值.因此,当且仅当返回值为非负且小于时,才完全写入以null结尾的输出n.C11dr§7.21.6.53

强大的代码将直接检查罕见编码错误的负值. if (some_int <= some_size_t)遗憾的是还不足以int将其转换为size_t.一个int返回值为负,则成为一个大的正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);
}
Run Code Online (Sandbox Code Playgroud)

不匹配的标志

一些编译器警告抱怨比较不同符号的整数,如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);
}
Run Code Online (Sandbox Code Playgroud)

C没有指定正值int是子范围的size_t. size_t可能会unsigned short然后SIZE_MAX < INT_MAX.(我知道没有这样的实现.)因此,演员(size_t) some_int可以改变价值.相反,将正返回值转换为unsigned(INT_MAX <= UINT_MAX始终为true)将不会更改该值,并将确保使用unsigned和之间的最宽无符号类型进行比较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");
  exit(EXIT_FAILURE);
}
Run Code Online (Sandbox Code Playgroud)