有没有像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 个步骤:
将缓冲区缩放到每个参数的预期最大输出大小。大几个字节应该没问题。
// 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);
确保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 它)。
为了生成文件名,我需要为 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 个步骤:
将缓冲区缩放到每个参数的预期最大输出大小。大几个字节应该没问题。
// 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);
确保
不会溢出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 它)。