有没有像g_printf_string_upper_bound这样的便携函数?

Is there a portable function like g_printf_string_upper_bound?

为了生成文件名,我需要为 sprintf 提供一些缓冲内存。这些缓冲区的大小在过去是任意选择的。这很容易在未来导致非常讨厌的堆栈溢出错误,例如int 变为 64 位长,但字符串缓冲区大小仅选择为 10 个字符,因为这是 32 位 int 可以容纳的最大数字量。

一些MWE:

  for (int i = 0; i < mpi_size; i++) {
     //magic number: 32bit integer has 10 digits, 
     //+6 for "/rank_", +1 for null termination
     char path2[strlen(path) + 17];
     //This can possibly be an access violation, or a very hard to
     //find bug:
     sprintf(path2, "%s/rank_%d", path, i);
     //Using path2 to access some file
  }

在其他地方选择的尺寸完全不同,人们非常确定 int 不会比例如大。 3 位数。这会更容易导致问题。

什么是完美且便携的解决方案?

我在 gnome 库中找到了 g_printf_string_upper_bound 函数,它可以优雅可靠地解决这个问题。

C 标准、POSIX 或其他地方是否有类似的内容?

考虑asprintf()。它将根据需要分配 need space。唯一预期的失败是 运行 内存不足。它不是标准的 C 函数,但在许多 *nix 系统上可用。否则:

建议 2 个步骤:

  1. 将缓冲区缩放到每个参数的预期最大输出大小。大几个字节应该没问题。

    // e.g. size needed to print INT_MIN
    #define INT_SIZE_MAX (sizeof(int)*CHAR_BIT/3 + 3)
    
    const char rank[] = "/rank_";
    char path2[strlen(path) + sizeof rank + INT_SIZE_MAX + 1];
    sprintf(path2, "%s%s%d", path, rank, i);
    
  2. 确保snprinf()

    不会溢出
    int n = snprintf(path2, sizeof path2, "%s%s%d", path, rank, i);
    if (n >= sizeof path2 || n < 0) HandleRareFailure();
    

snprintf() 可能失败的原因:
1) 某些字节序列无效(编码错误)。
2) 区域设置更改导致逗号添加到 %d --> "1,234,567,890".
3) 月相。 请参阅以下评论。

Is there anything like this in the C standard, [or] in POSIX?

没有

or somewhere else?

嗯,在 Gnome 库中有类似的东西……:P

尽管被广泛误解,snprintf 是专门为此类情况设计的。

如果缓冲区足够大,snprintf 中的 return 值是 写入的长度(不包括尾随 NUL)拿着它。因此,您可以分两步使用它:使用空缓冲区调用一次以找到所需的长度,使用它来分配必要的 space,然后再次调用它以产生结果:

size_t length = snprintf(NULL, 0, "%s/rank_%d", path, i) + 1;

char path2[length];

snprintf(path2, length, "%s/rank_%d", path, i);

至于这是使用 snprintf 的预期方式,是的,我很确定它确实如此。我的声明基于与 Peter Seebach 的对话,他说当他加入 C 标准委员会时,他的主要目的是让 snprintf 进入标准。

就此而言,我可能不得不对这种方法承担一点责任,我承认这是一种拙劣的做法。在发明 snprintf 之前,我写了 a post on comp.lang.c.moderated 来展示即使没有 snprintf 也能做大致相同的事情。为此,它打开一个临时文件并将输出写入其中以获取 return 值,然后使用 malloc 分配缓冲区,最后使用 sprintf 放置数据进入缓冲区。

snprintf 使用相同的基本思想,但无需打开外部文件即可使用它。尽管如此,它仍然使用了我 post 编辑的大部分技术,据我所知,我是第一个提出这种通用方法的人(尽管我很容易相信其他人可能首先想到了它,但是太丢人了 post 它)。