如何为 returns 字符串结果的 sprintf 制作包装器?

How to make a wrapper for sprintf that returns the string result?

我希望能够将格式化的字符串传递给其他函数,例如我的错误处理程序:

error_handler(str_format("error code %d in graphics function: %s", code, error));

我的第一个想法是使用 sprintf,但它没有 return 结果,所以你必须添加额外的行:

char msg = [100];
sprintf(msg, "error code %d in graphics function: %s", code, error)
error_handler(msg);

如果错误消息大于 msg 的大小,则可能存在问题。

所以我想我可以做一个像这样的包装函数:

char* str_format (char* format, ...) {
  char* output = malloc(100);
  va_list args;
  va_start(args, format);
  vsnprintf(output, 100, format, args);
  va_end(args);
  return output;
}

但我仍然有不知道输出所需大小的问题。所以我想我可以使用 sizeof 来获取格式的大小和所有 args,但是没有办法检测有多少 args,除非你每次都手动传递一些 args。我可以读取格式字符串并查找“%”符号并检测每个占位符和类型,但这似乎太复杂了,只是为了获取 sprintf 的 return 值。

我知道有一个名为 snprintf 的函数是安全的并且会限制字符数,但是 c

中没有 vsnprintf

EDIT: #1 将输出更改为 malloc #2 毕竟有一个 vsnprintf 函数,所以我改成了那个。那么问题就在于是否有比这种类型的包装函数更简单的方法。谢谢

有没有简单的方法让 sprintf 变成 return 格式化的字符串?

通常,您为此调用两次 vsnprintf -- 第一次调用没有缓冲区以获得所需的大小,第二次调用填充缓冲区

char* str_format (char* format, ...) {
  va_list args;
  va_start(args, format);
  int len = vsnprintf(0, 0, format, args);
  va_end(args);
  char *output = malloc(len+1);
  va_start(args, format);
  vsnprintf(output, len+1, format, args);
  va_end(args);
  return output;
}

注意调用者需要释放分配的指针(否则会泄漏)

So I thought I could make a wrapping function like this:

/* !!! DON'T DO THIS !!! */
char* str_format (char* format, ...) {
  char output[100];
  va_list args;
  va_start(args, format);
  vsprintf(output, format, args);
  va_end(args);
  return output;
}

这很糟糕,原因有很多:首先,正如您所说,您不知道输出的大小。然而,最重要的是,您不可能 return 本地定义的变量(如 output)。

要知道输出的大小,您可以首先对 vsnprintf 进行“虚拟”调用,大小为 0according to the manual 应该简单地计算并且 return 输出缓冲区所需的大小。

根据 return 一个有效的指针,您可以通过两种不同的方式完成这项工作:

  1. 使用 malloc 分配缓冲区,然后 return 指向已分配缓冲区的指针。这可能很烦人,因为您需要始终 free() 分配的缓冲区,并且由于您想像 error_handler(str_format(...)) 那样使用函数,因此您失去了对 returned 缓冲区的引用,因此您要么 free() 它在 error_handler() 中并始终假设 error_handler() 将采用 malloc 对象,或者您需要一个临时变量。

  2. 使用 static 缓冲区。然而,这有一个问题,即需要在编译时预先分配一个恒定大小。您可以选择此选项并限制错误消息的大小。这种方法的另一个限制是一次只能格式化一个错误(即这不是线程安全的),因为您每次都会覆盖相同的 static 缓冲区。这在 C 库中很常见,因为它无痛且易于实现,但只有在您有合理大小的错误消息时才真正适用。

选项 1(省略错误检查):

char* str_format(char* format, ...) {
  char *buf;
  int size;
  va_list args;

  va_start(args, format);
  size = vsnprintf(NULL, 0, format, args) + 1;
  va_end(args);

  buf = malloc(size);

  va_start(args, format);
  vsnprintf(NULL, size, format, args);
  va_end(args);

  return buf;
}

选项 2:

char* str_format(char* format, ...) {
  static char buf[1024]; // fixed at compile time
  va_list args;

  va_start(args, format);
  vsnprintf(NULL, 1024, format, args);
  va_end(args);

  return buf;
}

请注意,对于 vsnprintf,您需要至少定义以下功能测试宏之一,如手册所述:

#define _XOPEN_SOURCE 500
// or
#define _ISOC99_SOURCE      
// or
#define _BSD_SOURCE // old glibc <= 2.19