отслеживание того, сколько памяти выделено malloc - PullRequest
3 голосов
/ 09 января 2010

После быстрого просмотра связанных вопросов по SO, я пришел к выводу, что нет функции, которая бы проверяла объем памяти, выделенный malloc для указателя. Я пытаюсь воспроизвести некоторые базовые функции std :: string (в основном, динамического размера), используя простые символы char * в C, и не хочу постоянно вызывать realloc. Я думаю, мне нужно будет отслеживать, сколько памяти было выделено. Чтобы сделать это, я рассматриваю создание typedef, который будет содержать саму строку и целое число с объемом выделенной памяти, что-то вроде этого:

typedef struct {
    char * str;
    int mem;
} my_string_t;

Это оптимальное решение, или, может быть, вы можете предложить что-то, что принесет лучшие результаты? Заранее спасибо за помощь.

Ответы [ 6 ]

5 голосов
/ 09 января 2010

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

Должно быть выделено место для символов строки.

Например:

typedef struct
{
    int num_chars;
    char string[];
} my_string_t;

my_string_t * alloc_my_string(char *src)
{
    my_string_t * p = NULL;
    int N_chars = strlen(src) + 1;

    p = malloc( N_chars + sizeof(my_string_t));
    if (p)
    {
         p->num_chars = N_chars;
         strcpy(p->string, src);
    }
    return p;
}

В моем примере, чтобы получить доступ к указателю на вашу строку, вы обращаетесь к string члену my_string_t:

my_string_t * p = alloc_my_string("hello free store.");
printf("String of %d bytes is '%s'\n", p->num_chars, p->string);

Будьте осторожны, чтобы понять, что вы получаете указатель на строку в результате выделения места для хранения символов. Ресурс, который вы выделяете, является хранилищем символов, полученный указатель является ссылкой на выделенное хранилище.

В моем примере выделенная память распределяется последовательно следующим образом:

+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+
| 00 | 00 | 00 | 11 | 'h'| 'e'| 'l'| 'l'| 'o'| 20 | 'f'| 'r'| 'e'| 'e'| 20 | 's'| 't'| 'o'| 'r'| 'e'| '.'| 00 |
+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+
^^                   ^
||                   |
p|                   |
 p->num_chars        p->string

Обратите внимание, что значение p->string не сохраняется в выделенной памяти, оно находится в четырех байтах от начала выделенной памяти, непосредственно после (предполагаемого 32-разрядного, четырехбайтового) целого числа.

Ваш компилятор может потребовать, чтобы вы объявили гибкий C-массив следующим образом:

typedef struct
{
    int num_chars;
    char string[0];
} my_string_t;

но версия без нуля предположительно совместима с C99.

Вы можете выполнить эквивалентную вещь без элемента массива следующим образом:

typedef struct
{
    int num_chars;
} mystr2;

char * str_of_mystr2(mystr2 * ms)
{
    return (char *)(ms + 1);
}

mystr2 * alloc_mystr2(char *src)
{
    mystr2* p = NULL;
    size_t N_chars = strlen(src) + 1;

    if (N_chars num_chars = (int)N_chars;
         strcpy(str_of_mystr2(p), src);
    }
    return p;
} 

printf("String of %d bytes is '%s'\n", p->num_chars, str_of_mystr2 (p));

Во втором примере значение, эквивалентное p->string, рассчитывается по str_of_mystr2(). Он будет иметь примерно то же значение, что и в первом примере, в зависимости от того, как конец структур будет упакован вашими настройками компилятора.

Хотя некоторые предлагали бы отслеживать длину в size_t, я бы посмотрел статью старого доктора Добба о том, почему я не согласен. Поддержка значений, превышающих INT_MAX, сомнительна для правильности вашей программы. Используя int, вы можете написать assert(p->num_chars >= 0); и что-то проверить. В случае без знака вы бы написали эквивалентный тест, например, assert(p->num_chars < UINT_MAX / 2); Пока вы пишете код, который содержит проверки данных во время выполнения, может быть полезным использование подписанного типа.

С другой стороны, если вы пишете библиотеку, которая обрабатывает строки, превышающие символы UINT_MAX / 2, я приветствую вас.

2 голосов
/ 09 января 2010

Это очевидное решение. И пока вы занимаетесь этим, вы можете захотеть иметь член структуры, который поддерживает фактически используемый объем выделенной памяти. Это избавит от необходимости постоянно вызывать strlen () и позволит вам поддерживать строки, не заканчивающиеся нулем, как это делает класс C ++ std :: string.

1 голос
/ 09 января 2010

Я думаю, вы могли бы использовать malloc_usable_size .

1 голос
/ 09 января 2010

написать функции-оболочки. Если вы используете malloc, вам все равно следует это сделать.

Для примера посмотрите в «Написание твердого кода»

1 голос
/ 09 января 2010

Более распространенный способ - обернуть malloc (и realloc) и сохранить список размеров и указателей
Таким образом, вам не нужно менять какие-либо строковые функции.

1 голос
/ 09 января 2010

Вот как это было сделано в плейстоцене, и именно так вы должны делать это сегодня. Вы мертвы на деньги, которые malloc не предлагает ни одного переносимого, поддерживаемого механизма для запроса размера выделенного блока.

...