Динамическое размещение в C - PullRequest
4 голосов
/ 18 мая 2010

Я пишу программу, и у меня возникает следующая проблема:

char *tmp;
sprintf (tmp,"%ld",(long)time_stamp_for_file_name);

Может ли кто-нибудь объяснить, сколько памяти выделено для строки tmp.

Сколько символов длинная переменная?

Спасибо,

Буду признателен также за ссылку на исчерпывающий ресурс по этой информации.

Спасибо

UPDATE:

Используя ваши примеры, я получил следующую проблему:

root@-[/tmp]$cat test.c

       #include <stdio.h>
       int
       main()
       {
            int len;
            long time=12345678;
            char *tmp;
            len=snprintf(NULL,0,"%ld",time);
            printf ("Lunghezza:di %ld %d\n",time,len);      
            return 0;
       }

root@-[/tmp]$gcc test.c
root@-[/tmp]$./a.out 
Lunghezza:di 12345678 -1
root@-[/tmp]$

Таким образом, результат len из snprintf равен -1, я скомпилировал на Solaris 9 со стандартным компилятором.

Пожалуйста, помогите мне!

Ответы [ 7 ]

6 голосов
/ 18 мая 2010

Если ваш компилятор соответствует C99, вы должны иметь возможность:

char *tmp;
int req_bytes = snprintf(NULL, 0, "%ld",(long)time_stamp_for_file_name);
tmp = malloc(req_bytes +1); //add +1 for NULL
if(!tmp) {
    die_horrible_death();
}
if(snprintf(tmp, req_bytes+1, "%ld",(long)time_stamp_for_file_name) != req_bytes) {
    die_horrible_death();
}

Соответствующие части стандарта (из проекта ):

  • 7.19.6.5.2: Если n равно нулю, ничего не записывается, а s может быть нулевым указателем.
  • 7.19.6.5.3: функция snprintf возвращает количество символов, которое было бы написано если бы n было достаточно большим, не считая завершающего нулевого символа или отрицательного значение, если произошла ошибка кодирования. Таким образом, нулевой вывод был полностью записано в том и только в том случае, если возвращаемое значение неотрицательно и меньше n.

Если это не работает, я полагаю, ваш компилятор / libc не поддерживает эту часть c99, или вам может потребоваться явно включить ее. Когда я запускаю ваш пример (с gcc версии 4.5.0 20100610 (предварительная версия), Linux 2.6.34-ARCH), я получаю

$./example
Lunghezza:di 12345678 8

5 голосов
/ 18 мая 2010

Количество фактически используемых символов, очевидно, зависит от значения: если time_stamp_for_file_name равно 0, то на самом деле нужно 2 байта. Если есть сомнения, вы можете использовать snprintf, который говорит вам, сколько места вам нужно:

int len = snprinf(0, 0, "%ld", (long)time_stamp_for_file_name) + 1;
char *tmp = malloc(len);
if (tmp == 0) { /* handle error */ }
snprintf(tmp, len, "%ld", (long)time_stamp_for_file_name);

Остерегайтесь реализаций, где snprintf возвращает -1 для недостаточного пространства, а не для требуемого пространства.

Однако, как говорит Пол Р., вы можете определить фиксированную верхнюю границу, основанную на размере long вашей реализации. Таким образом вы полностью избегаете динамического размещения. Например:

#define LONG_LEN (((sizeof(long)*CHAR_BIT)/3)+2)

(основываясь на том факте, что log-2 в log 10 больше 3). Это +2 дает вам 1 для знака минус и 1 для того факта, что целочисленное деление округляется в меньшую сторону. Вам понадобится еще 1 для нулевого терминатора.

Или:

#define STRINGIFY(ARG) #ARG
#define EXPAND_AND_STRINGIFY(ARG) STRINGIFY(ARG)

#define VERBOSE_LONG EXPAND_AND_STRINGIFY(LONG_MIN)
#define LONG_LEN sizeof(VERBOSE_LONG)

char tmp[LONG_LEN];
sprintf(tmp, "%ld", (long)time_stamp_for_file_name);

VERBOSE_LONG может быть немного больше, чем вам нужно. На моем компиляторе это (-2147483647L-1). Я не уверен, может ли LONG_MIN расширяться до чего-то вроде шестнадцатеричного литерала или встроенного компилятора, но если это так, то он может быть слишком коротким, и этот трюк не сработает. Тем не менее, достаточно просто выполнить юнит-тестирование.

Если вы хотите, чтобы жесткая верхняя граница охватывала все возможности стандарта, до определенного предела, вы можете попробовать что-то вроде этого:

#if LONG_MAX <= 2147483647L
    #define LONG_LEN 11
#else
    #if LONG_MAX <= 4294967295L
        #define LONG_LEN 11
    #else
        #if LONG_MAX <= 8589934591L
            ... etc, add more clauses as new architectures are
                     invented with bigger longs
        #endif
    #endif
#endif

Но я сомневаюсь, что оно того стоит: лучше просто определить его в каком-то заголовке переносимости и настроить вручную для новых платформ.

5 голосов
/ 18 мая 2010

Сложно сказать заранее, хотя я предполагаю, что вы могли бы предположить, что оно будет не более 64 бит, и, следовательно, "18,446,744,073,709,551,615" должно быть максимально возможным значением. Это 2 + 6 * 3 = 20 цифр, запятые обычно не включены. Было бы 21 для отрицательного числа. Итак, перейдите на 32 байта в качестве хорошего и круглого размера.

Лучше было бы связать это с использованием snprintf(), поэтому вы не получите переполнение буфера, если ваша оценка выключена.

3 голосов
/ 18 мая 2010

Это зависит от размера long в вашей системе. Предполагая наихудший случай из 64 битов, вам нужно максимум 22 символа - это допускает 20 цифр, предшествующий - и завершающий \0. Конечно, если вы чувствуете себя экстравагантно, вы всегда можете добавить немного больше и сделать из этого круглое число, например 32.

2 голосов
/ 18 мая 2010

Для представления десятичной цифры требуется лог 2 10 (~ 3,32) бита; таким образом, вы можете вычислить количество цифр следующим образом:

#include <limits.h>
#include <math.h>

long time;
double bitsPerDigit = log10(10.0) / log10(2.0); /* or log2(10.0) in C99 */
size_t digits = ceil((sizeof time * (double) CHAR_BIT) / bitsPerDigit);

char *tmp = malloc(digits+2); /* or simply "char tmp[digits+2];" in C99 */

"+2" означает знак и терминатор 0.

1 голос
/ 18 мая 2010

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

sizeof (long) дает вам количество байтов при компиляции. Умножьте это на 8, чтобы получить биты. Добавьте два, прежде чем разделить на три, чтобы получить потолок вместо пола. Помните, что строки C хотят получить последний нулевой байт до конца, поэтому добавьте один к результату. (Еще один для негатива, как описано в комментариях).

char tmp[(sizeof(long)*8+2)/3+2];
sprintf (tmp,"%ld",(long)time_stamp_for_file_name);
0 голосов
/ 07 июля 2010

3*sizeof(type)+2 является безопасным общим правилом для количества байтов, необходимого для форматирования целого типа type в виде десятичной строки, причина в том, что 3 является верхней границей log10(256) и n -байта целое число равно n цифр в базе-256 и, следовательно, ceil(log10(256^n))==ceil(n*log10(256)) цифр в базе 10. +2 предназначен для учета завершающего байта NUL и возможного знака минуса, если type очень мало.

Если вы хотите быть педантичным и поддерживать DSP и тому подобное с CHAR_BIT!=8, тогда используйте 3*sizeof(type)*((CHAR_BIT+7)/8)+2. (Обратите внимание, что для систем POSIX это не имеет значения, поскольку POSIX требует UCHAR_MAX==255 и CHAR_BIT==8.)

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...