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

Моя программа достаточно хороша для моего задания, но я знаю, что это не хорошо

Я только начинаю задание для uni, и он поднял вопрос для меня.

Я не понимаю, как вернуть строку из функции без утечки памяти.

char* trim(char* line)  {
    int start = 0;
 int end = strlen(line) - 1;

 /* find the start position of the string */
 while(isspace(line[start]) != 0)  {
     start++;
 }
 //printf("start is %d\n", start);

 /* find the position end of the string */
 while(isspace(line[end]) != 0)  {
     end--;
 }
 //printf("end is %d\n", end);

 /* calculate string length and add 1 for the sentinel */
 int len = end - start + 2;

 /* initialise char array to len and read in characters */
 int i;
 char* trimmed = calloc(sizeof(char), len);

 for(i = 0; i < (len - 1); i++)  {
     trimmed[i] = line[start + i];
 }
 trimmed[len - 1] = '\0';

    return trimmed;
}

как вы можете видеть, я возвращаю указатель на char, который является массивом. Я обнаружил, что если бы я попытался сделать массив "обрезанный" чем-то вроде:

char trimmed[len];

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

Поэтому вместо этого я создал свой массив, выделив некоторую память указателю char.

Я понимаю, что эта функция, вероятно, неоптимальна для того, что она пытается сделать, но я действительно хочу знать:

  • Можно ли инициализировать массив с помощью переменной, чтобы объявить длину, например:

    char обрезано [len];

  • Если бы у меня был массив такого типа (char trimmed []), он имел бы тот же тип возврата, что и указатель на char (т.е. char *).

  • Если я создаю свой массив, выделив некоторую память и выделив ее указателю char, как мне освободить эту память. Мне кажется, что как только я верну этот массив, я не могу получить доступ к нему, чтобы освободить его, поскольку это локальная переменная.

4b9b3361

Ответ 1

Чтобы адресовать (3) - вы можете free вновь выделенную строку из вашего кода вызова, как только вы закончите с ней:

char* tmp = trim(myline);

if (tmp != NULL) {
    ....
    free( tmp );
}

Но это накладывает нагрузку на вызывающего, чтобы не забыть освободить память. Поэтому вместо этого вы можете передать выделенный буфер и размер буфера trim(), например:

void trim(char* line, char *trimmed_buf, int trimmed_buf_len){ ... }

Нейл отлично справился с вашими другими вопросами. В основном объявление массива char trimmed[len]; объявит локальную переменную в стек , поэтому, если синтаксически корректно возвращать char * в эту память, то местоположение памяти, на которое оно указывает, больше не будет действительный.

Ответ 2

Чтобы ответить на ваши конкретные вопросы, синтаксис:

char trimmed[len];

где len - переменная, разрешена только на C99, а не на C89 или на С++. Тип возврата действительно был бы char *, но возврат локальной переменной trimmed приведет к поведению undefined, поэтому не делайте этого. И если вы распределяете массив динамически в функции с помощью calloc и возвращаете его, то вызывающая функция освобождает его, используя указатель, возвращаемый функцией.

Ответ 3

Что касается динамической калибровки объявления массива типа char trimmed[len];, то самая новая версия стандарта C (ISO/IEC 9899: 1999) позволяет, но для этой функции это не помогло бы вообще. Переменная trimmed имеет свою область действия внутри функции trim, и она выделяется в стеке. Поэтому, если вы поместите return trimmed; в свой код, вы вернете указатель на переменную в стеке, а часть стека, в которой находится эта переменная, будет выпущена в тот момент, когда функция вернется, так что это не получится так хорошо...

Ответ 4

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

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

Ответ 5

Помимо решений на С++, таких как использование std::string, вы всегда можете выделить массив заданного размера и передать размер в качестве параметра, а массив как параметр по ссылке или как указатель?

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

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

Ответ 6

Выделение и удаление вне функции:

Выделите его из стека с char обрезанным [SIZE] и передайте его функции или из кучи с calloc и передайте ее функции.

Выделение внутри функции и удаление снаружи:

Вы выделяете его внутри функции с помощью calloc, но вызывающий должен освободить ее, используя бесплатный.

Ответ 7

Hm, ну, ответы на ваши вопросы:

  • да, массив будет выделен в стеке, а не в кучу и будет освобожден при возврате функции.
  • да, char [] в основном эквивалентен char *. Лучше держать их отдельно, так как существует семантическая разница.
  • Используя любой указатель на память, вы можете использовать бесплатный. В вашем случае вы освободите функцию возврата. Это считается довольно плохой формой и склонностью к ошибкам. Обычно вы хотите, чтобы allocat'er был свободным() er. Возможно, вы могли бы пройти в буфер для нового пространства, или вызывающий объект мог согласиться с содержимым char * line, написанным поверх существующего содержимого. Это работает, поскольку trim() всегда удаляет только вещи. Я думаю, что пройденный в буфере будет работать чаще, и это хорошая вещь, чтобы привыкнуть.

Что касается вашей функции, рассмотрите возможность использования memcpy() или его кузенов для копирования байтов в буфер char и из него.