Определение размера буфера sprintf - какой стандарт? - PullRequest
40 голосов
/ 13 октября 2010

При преобразовании типа int, например, так:

char a[256];
sprintf(a, "%d", 132);

Каков наилучший способ определения размера a ?Я предполагаю, что ручная настройка это хорошо (как я видел это используется везде), но насколько оно должно быть большим?Какое наибольшее значение int возможно в 32-битной системе, и есть ли какой-нибудь хитрый способ определить это на лету?

Ответы [ 7 ]

73 голосов
/ 13 октября 2010

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

int size = snprintf(NULL, 0, "%d", 132);
char * a = malloc(size + 1);
sprintf(a, "%d", 132);

Я разобью, что здесь происходит.

  1. В первой строке мы хотим определить, сколько символов нам нужно.Первые 2 аргумента snprintf говорят, что я хочу записать 0 символов результата в NULL.Когда мы сделаем это, snprintf на самом деле нигде не будет писать никаких символов, он просто вернет количество символов, которое было бы написано.Это то, что мы хотели.
  2. Во второй строке мы динамически распределяем память для указателя char.Удостоверьтесь и добавьте 1 к необходимому размеру (для завершающего \0 завершающего символа).
  3. Теперь, когда для указателя char выделено достаточно памяти, мы можем смело использовать sprintf для записицелое число к указателю char.

Конечно, вы можете сделать его более кратким, если хотите.

char * a = malloc(snprintf(NULL, 0, "%d", 132) + 1);
sprintf(a, "%d", 132);

Если это не «быстрая и грязная» программа, вывсегда нужно обязательно освободить память, которую вы вызывали с помощью malloc.Это где динамический подход усложняется с C. Однако, IMHO, если вы не хотите выделять огромные char указатели, когда большую часть времени вы будете использовать только очень небольшую их часть, тогда я не будуНе думаю, что это плохой подход.

14 голосов
/ 18 июня 2015

Можно заставить работать решение Daniel Standage для любого количества аргументов, используя vsnprintf , который находится в C ++ 11 / C99.

int bufferSize(const char* format, ...) {
    va_list args;
    va_start(args, format);
    int result = vsnprintf(NULL, 0, format, args);
    va_end(args);
    return result + 1; // safe byte for \0
}

Как указано в стандарт c99 , раздел 7.19.6.12:

Функция vsnprintf возвращает количество написанных символов, если бы n было достаточно большим, не считая завершающий нулевой символ или отрицательное значениеесли произошла ошибка кодирования
.

12 голосов
/ 13 октября 2010

Максимально возможное число битов в int равно CHAR_BIT * sizeof(int), а десятичная цифра "стоит" не менее 3 бит, поэтому свободная верхняя граница пространства, необходимого для произвольного int, равна (CHAR_BIT * sizeof(int) / 3) + 3.Это +3 для того, что мы округлили при делении, одно для знака, одно для нулевого терминатора.

Если под «в 32-битной системе» вы подразумеваете, что int32 бита, тогда вам нужно 12 байтов.10 для цифр, один для знака, один для нулевого терминатора.

В вашем конкретном случае, когда преобразуемое int равно 132, вам нужно 4 байта.Badum, tish.

Если буферы фиксированного размера могут использоваться с разумной границей, они являются более простым вариантом.Я не так скромно утверждаю, что приведенная выше граница является разумной (13 байт вместо 12 для 32-битного int и 23 байта вместо 21 для 64-битного int).Но для сложных случаев, в C99 вы могли бы просто позвонить snprintf, чтобы получить размер, а затем malloc.Это слишком для такого простого случая, как этот.

8 голосов
/ 19 июня 2012

Я вижу, что этому разговору пару лет, но я нашел его, пытаясь найти ответ для MS VC ++, где snprintf не может быть использован для определения размера. Я выложу ответ, который наконец нашел, на случай, если он кому-нибудь поможет:

VC ++ имеет функцию _scprintf специально для поиска необходимого количества символов.

2 голосов
/ 25 февраля 2013

Если вы печатаете простое целое число и ничего больше, есть гораздо более простой способ определить размер выходного буфера.По крайней мере, в вычислительном отношении проще, код немного тупой:

char *text;
text = malloc(val ? (int)log10((double)abs(val)) + (val < 0) + 2 : 2);

log10 (значение) возвращает количество цифр (минус одна), необходимое для хранения положительного ненулевого значения в базе 10. Это немного уходитрельсы для чисел меньше одного, поэтому мы указываем abs () и код специальной логики для нуля (троичный оператор, test? truecase: falsecase).Добавьте один для пробела для хранения знака отрицательного числа (val <0), один, чтобы составить отличие от log10, и другой для нулевого терминатора (для общего количества 2), и вы только что рассчиталиточный объем памяти, требуемый для данного числа, без вызова snprintf () или эквивалентов <em>дважды для выполнения работы.Кроме того, он обычно использует меньше памяти, чем потребуется INT_MAX.

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

Также обратите внимание, что вам может не понадобиться (double) в отличие от (float).Я не стал проверять.Подобное приведение туда-сюда может также стать проблемой.YMMV на все это.Тем не менее, прекрасно работает для меня.

1 голос
/ 13 октября 2010

Хорошо, что вы беспокоитесь о размере буфера. Чтобы применить эту мысль в коде, я бы использовал snprintf

snprintf( a, 256, "%d", 132 );

или

snprintf( a, sizeof( a ), "%d", 132 );  // when a is array, not pointer
0 голосов
/ 13 октября 2010

Во-первых, спринтф - это дьявол.Во всяком случае, используйте snprintf, иначе вы рискуете перегрузить память и вывести из строя ваше приложение.

Что касается размера буфера, он, как и все другие буферы - настолько мал, насколько это возможно, настолько большой, насколько это необходимо.В вашем случае у вас есть целое число со знаком, поэтому возьмите максимально возможный размер и не стесняйтесь добавить немного защитного отступа.Не существует «стандартного размера».

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

...