Если у вас есть 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);
}