Лучший способ структурировать код C, где mallo c используется внутри функции, но свободен при вызове функции - PullRequest
0 голосов
/ 15 апреля 2020

У меня есть функция, которая заполняет динамические c массивы на основе содержимого двоичного файла. Я использую malloc для определения размера массива строк внутри функции, а затем возвращаю указатель и информацию о размере обратно вызывающей функции. Поскольку это массив строк, я использую массив указателей, поэтому для указателя массива вызывается malloc, а для каждого члена malloc.

т.е. в моей функции у меня есть этот блок кода. Функция возвращает p_count и p_tokens вызывающей стороне.

    // Allocate memory:
    *p_tokens = malloc(*p_count * sizeof(char *));
    for (int i = 0; i < (int)*p_count; i++) {
        (*p_tokens)[i] = malloc(TOKEN_LENGTH);
    }

Это делает необходимым использование free из вызывающего кода, но, конечно, становится немного грязно. Каков наилучший способ справиться с этим сценарием? Должен ли я создать отдельную функцию для освобождения всех указателей, сгенерированных в моей исходной функции? Должен ли я просто иметь дело с кластером свободных циклов в конце моего вызывающего кода, или я все делаю неправильно?

Ответы [ 3 ]

1 голос
/ 15 апреля 2020

Вы должны , убедитесь, что malloc() s связаны с free() s. Где вы пишете их, совершенно неважно, действительно важный момент заключается в том, что код структурирован так, что легко проверить (т. Е. Трудно (ошибочно) ошибиться) их баланс. Например, вокруг идеи «создавать (и распределять), использовать, потреблять (и бесплатно)».

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

Еще один способ справиться с этим (при некоторых затратах производительности) - использовать некоторую библиотеку для сбора мусора, например, сборщик мусора Boehm для C и C ++ (подробный пример приведен в «Сборка мусора в C программах» от Insolvible, Linux Journal, aug 2003). Сборщик мусора доступен в большинстве Linux дистрибутивов, будучи открытым исходным кодом, его можно собирать и устанавливать в большинстве систем.

1 голос
/ 16 апреля 2020

Запись free_token_array(token *array, size_t count), которая освободит токены и массив в том же модуле, что и ваша функция распределения, имеет смысл. Таким образом, вы можете легко сохранять их в синхронизации c, если измените стратегии распределения.

Также обратите внимание, что вам следует избегать приведения *p_count в распределении l oop, просто напишите:

    for (size_t i = 0; i < *p_count; i++)
0 голосов
/ 15 апреля 2020

В идеале вы должны где-то контролировать статус ваших «строк». Структура: указатели + флаги для существования + флаги для инициализации + флаги для доступа.

struct arrstring {
    p_string * Strings;
    // flags of allocation
    p_exist * ExistFlag;
    // flags tracking initialization with values
    p_init * InitStrings;
    // flags allowing access/lock
    p_access * AccessF;
    p_mutex * LockStrings;
}

Таким образом, вы можете создать все строки одним вызовом 'callo c ()', но инициализировать их позже .

PS.

Здесь, в sizeof (char *) вы берете размер указателя, я не знаю ваших намерений, но это выглядит как недоразумение. Вы можете определить максимальный размер вашей строки, например, 512 байт, и выделить его с помощью mallo c (MAX_SIZE * sizeof (char)).

Также использование 'char' для строк является устаревшей функцией. Подходит для тестирования, но сегодня существуют реальные символы, и «char» используется только как акроним «байта» (это работает, потому что UTF-8 является общепринятым, а наименьшие значения размера UTF-8 выбираются одновременно с ASCII).

PS2. Стандарт C17 подтверждает, что "uchar.h" и "wchar.h" широко приняты и поддерживаются средами C. Таким образом, пользователи могут иметь дело с символами соответственно, если они выходят за пределы типа 'char'.

PS3. Ну, а про C стандарты про персонажей. C всегда были "молчаливы", когда речь заходит о персонажах, и различные системы решают это по-разному. Таким образом, способ, которым C работает со строками, называется "C strings", чтобы различать guish it. В «C strings» длина строк всегда измеряется в «char» или «wchar_t», которые имеют фиксированный размер, , даже если сама строка кодируется с переменной шириной , это может привести к проблемам, потому что символы большой ширины будут интерпретироваться как несколько символов. Но размер «char» всегда будет фиксироваться сам, на некоторых машинах он был 6 бит, на некоторых машинах он был 12 бит. Теперь он в основном 8-битный, поэтому программисты в большинстве случаев называют его «целочисленным типом», и существует общая точка зрения, когда программисты полагают, что char = byte. Ну, это само по себе отражает эволюцию термина "байт".

...