不使用手动分配缓冲区的情况下使用 sprintf

9
在我正在开发的应用程序中,日志记录功能使用 sprintf 来格式化写入文件的文本。因此,类似以下的内容:
```c sprintf(buffer, "The value of x is %d", x); ```
将会被写入文件。
char buffer[512];
sprintf(buffer, ... );

当手动分配的缓冲区无法容纳发送的消息时,这有时会导致问题。

有没有一种方法可以在不像这样手动分配内存的情况下获得sprintf 的行为?

编辑:虽然sprintf是一个C操作,但我正在寻找C++类型的解决方案(如果有的话!)来获得此类行为...

5个回答

18

您可以使用asprintf(3)(注意:非标准)来为您分配缓冲区,因此您无需预先分配它。

您可以使用非标准的asprintf(3)函数,它会为您分配缓冲区,因此您不需要提前分配它。

请提及此函数在不同系统中的可用性,因为它既不是标准C库,也不是POSIX。 - qrdl
主要是Linux - 可能也包括BSD。还有一个vasprintf()函数。 - Jonathan Leffler
1
哦,对不起...原帖没有提到平台,所以我就随便说了。它可以在Linux、BSD和Mac OS X上找到...现在它是gnuc库的标准部分。 - Jason Coco

11

不,你不能使用 sprintf() 来分配足够的内存。以下是替代方案:

  • 使用 snprintf() 来截断消息 - 并不能完全解决你的问题,但可以防止缓冲区溢出问题
  • 将缓冲区加倍(或加三倍等等) - 除非你处于受限制的环境中
  • 使用 C++ 的 std::stringostringstream - 但你会失去 printf 格式,必须使用 << 运算符
  • 使用附带类似 printf 的 % 运算符的 Boost Format

4
请注意,snprintf() 会告诉您所需的空间大小,因此如果需要,您可以使用它两次,第一次使用静态缓冲区,如示例所示,第二次使用动态分配的缓冲区。 - Jonathan Leffler
3
第二个建议得到了-1分(将缓冲区大小加倍)。由于您不知道字符串的最大长度,即使您将其乘以十亿,也无法保证它不会溢出。 - finnw

5

我不知道有没有一种避免分配内存的版本,但如果C99 sprintf允许将NULL指针作为字符串参数传入。虽然效率不高,但这可以给你完整的字符串(只要有足够的内存可用),而不会出现溢出的风险:

length = snprintf(NULL, ...);
str = malloc(length+1);
snprintf(str, ...);

1
如果您使用的是C99,您也可以使用变长数组,因此不需要malloc str,只需在调用snprintf后声明它为“char str[length+1];”。然后无需释放它,因为它在堆栈上。 - Ben Combee
2
如果您正在使用MSVC运行时,您会发现snprintf返回负值而不是所需的长度。因此,如果您想在MSVC下获得所需的长度,您需要使用另一个函数_vscprintf [http://msdn.microsoft.com/en-us/library/w05tbk72.aspx]。 - Hasturkun

3
"日志记录工具使用sprintf对写入文件的文本进行格式化。"
"fprintf()没有任何大小限制。如果可以直接将文本写入文件,则应该这样做!"
"不过我猜测可能存在一些中间处理步骤。如果您知道需要多少空间,可以使用malloc()来分配相应的空间。"
"在这种情况下,一个技巧是分配一个合理大小的缓冲区(99% 的情况下足够大),如果还不够大,可以将数据分成几个块逐个处理。"

1
文件访问比内存访问要慢得多。这就是为什么中间sprintf不是一个坏主意的原因之一。 - xtofl
如果可用,通过缓冲文件可以获得大部分的好处。仍然存在调用开销,但至少不会频繁访问磁盘。 - Steve Jessop

1

使用基本版本的 sprintf,无论是手动分配还是在堆栈上分配内存,都无法防止数据覆盖传入的缓冲区。

为了防止缓冲区被覆盖,您需要使用更安全的 sprintf 版本之一,如 sprintf_s(仅适用于 Windows)

http://msdn.microsoft.com/en-us/library/ybk95axf.aspx


使用普通的sprintf()函数可以防止缓冲区溢出,例如:sprintf(buf, "%.*s", buf_size - 1, input_string); 但这仅适用于已知的、固定格式的字符串。 - finnw

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接