Правильное распределение памяти? - PullRequest
3 голосов
/ 13 сентября 2010

Как бы я выделил столько памяти, сколько действительно необходимо, не зная, насколько велики аргументы функции?

Обычно я бы использовал фиксированный размер и вычислял остальное с помощью 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 Ответ

7 голосов
/ 13 сентября 2010

Если у вас есть vsnprintf, я бы этим воспользовался. Он предотвращает переполнение буфера, так как вы указываете размер буфера, и возвращает фактический необходимый размер.

Итак, выделите свой буфер размером 1 КБ, затем попытайтесь использовать vsnprintf для записи в этот буфер, ограничивая размер. Если возвращенный размер был меньше или равен размеру вашего буфера, тогда он работает, и вы можете просто использовать буфер.

Если возвращенный размер был больше размера буфера, тогда вызовите realloc, чтобы получить больший буфер и попробуйте снова. Если данные не изменились (например, возникли проблемы с потоками), вторая будет работать нормально, поскольку вы уже знаете, насколько она будет большой.

Это относительно эффективно, если вы тщательно выбираете размер буфера по умолчанию. Если подавляющее большинство ваших выходных данных находится в пределах этого предела, требуется очень мало перераспределений (возможную оптимизацию см. Ниже).

Если у вас нет функции vsnprintf -типа, уловка, которую мы использовали ранее, - открыть дескриптор файла для /dev/null и использовать его для той же цели (проверка размер перед выводом в буфер). Используйте vfprintf для этого дескриптора файла, чтобы получить размер (вывод идет в область битов), затем выделите достаточно места на основе возвращаемого значения и vsprintf для этого буфера. Опять же, он должен быть достаточно большим, так как вы вычислили нужный размер.


Оптимизация вышеописанных методов будет заключаться в использовании локального буфера, а не выделенного буфера, для фрагмента 1 КБ. Это позволяет избежать необходимости использовать 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);
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...