为了生成文件名,我需要为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或其他地方有这样的东西吗?
答案 0 :(得分:3)
虽然被广泛误解,snprintf
专门用于解决此类情况。
snprintf
的返回值是 写入的长度(不包括尾随的NUL),如果缓冲区足够大以容纳它。因此,您可以分两步使用它:使用空缓冲区调用一次以找到所需的长度,使用它来分配必要的空间,然后再次调用它以产生结果:
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谈话的基础上,Peter Seebach表示,当他加入C标准委员会时,他的主要目的是让snprintf
进入标准。
子>
<子>
就此而言,我可能不得不对这种方法承担一点责任,我承认这是一种污点。在snprintf
被发明之前回来,我写了a post on comp.lang.c.moderated,显示即使没有snprintf
,如何做同样的事情。为此,它打开了一个临时文件并将输出写入它以获取返回值,然后使用malloc
分配缓冲区,最后使用sprintf
将数据放入缓冲区。
子>
<子>
snprintf
使用相同的基本思想,但无需打开外部文件即可使用它。尽管如此,它仍然使用了我发布的技术,据我所知,我是第一个提出这种一般方法的人(虽然我可以很容易地相信其他人可能首先想到它,但是发帖太惭愧了它)。
子>
答案 1 :(得分:2)
考虑asprintf()
。它将根据需要分配需求空间。唯一预期的失败是内存耗尽。它不是标准的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
- &gt; &#34; 1234567890&#34 ;.
3)月相。见下面的评论。
答案 2 :(得分:-6)
C标准中有这样的东西,[或]在POSIX中吗?
否强>
或其他地方?
Gnome图书馆里有类似的东西......:P