如何在不知道函数的参数有多大的情况下,如何分配尽可能多的内存?
通常,我会使用固定大小,并使用sizeof
计算其余部分(注意:代码不应该有意义,但要显示问题):
#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
int test(const char* format, ...)
{
char* buffer;
int bufsize;
int status;
va_list arguments;
va_start(arguments, format);
bufsize = 1024; /* fixed size */
bufsize = sizeof(arguments) + sizeof(format) + 1024;
buffer = (char*)malloc(bufsize);
status = vsprintf(buffer, format, arguments);
fputs(buffer, stdout);
va_end(arguments);
return status;
}
int main()
{
const char* name = "World";
test("Hello, %s\n", name);
return 0;
}
但是,我不认为这是要走的路......所以,我如何正确计算所需的缓冲区大小?
答案 0 :(得分:7)
如果您有vsnprintf
可用,我会利用它。它可以防止缓冲区溢出,因为您提供了缓冲区大小,并返回所需的实际大小。
因此,分配1K缓冲区然后尝试使用vsnprintf
写入缓冲区,限制大小。如果返回的大小小于或等于缓冲区大小,那么它可以工作,你可以使用缓冲区。
如果返回的大小大于而不是缓冲区大小,则调用realloc
以获得更大的缓冲区并再次尝试。如果数据没有改变(例如,线程问题),第二个将正常工作,因为你已经知道它有多大。
如果您仔细选择默认缓冲区大小,这是相对有效的。如果您的绝大多数输出都在此限制范围内,则必须进行少量重新分配(请参阅下面的可能的优化)。
如果不有vsnprintf
- 类型的函数,我们之前使用过的技巧是打开/dev/null
的文件句柄并将其用于相同的目的(在输出到缓冲区之前检查大小)。使用vfprintf
到该文件句柄来获取大小(输出转到位桶),然后根据返回值分配足够的空间,并vsprintf
到该缓冲区。同样,它应该足够大,因为你已经找到了所需的尺寸。
对上述方法的优化是为1K块使用本地缓冲区而不是分配的缓冲区。这避免了在不必要的情况下使用malloc
,假设您的堆栈可以处理它。
换句话说,使用类似的东西:
int test(const char* format, ...)
{
char buff1k[1024];
char *buffer = buff1k; // default to local buffer, no malloc.
:
int need = 1 + vsnprintf (buffer, sizeof (buff1k), format, arguments);
if (need > sizeof (buff1k)) {
buffer = malloc (need);
// Now you have a big-enough buffer, vsprintf into there.
}
// Use string at buffer for whatever you want.
...
// Only free buffer if it was allocated.
if (buffer != buff1k)
free (buffer);
}