适当的内存分配?

时间:2010-09-13 01:31:08

标签: c memory-management

如何在不知道函数的参数有多大的情况下,如何分配尽可能多的内存?

通常,我会使用固定大小,并使用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;
}

但是,我不认为这是要走的路......所以,我如何正确计算所需的缓冲区大小?

1 个答案:

答案 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);
}