Подтвердить что ты не робот

Определение размера буфера sprintf - что такое стандарт?

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

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

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

4b9b3361

Ответ 1

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

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

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

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

Ответ 2

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

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

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

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

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

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

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

Ответ 3

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

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 standard, раздел 7.19.6.12:

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

Ответ 4

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

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

Ответ 5

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

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

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

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

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

Ответ 6

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

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

или

snprintf( a, sizeof( a ), "%d", 132 );  // when a is array, not pointer

Ответ 7

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

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

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