Моя программа достаточно хороша для моего задания, но я знаю, что это не хорошо - PullRequest
29 голосов
/ 26 марта 2010

Я только начинаю назначение для 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 trimmed[len];

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

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

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

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

    char trimmed [len];

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

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

Ответы [ 7 ]

15 голосов
/ 26 марта 2010

По адресу (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 * в эту память, расположение памяти, на которое она указывает, больше не будет действительным .

6 голосов
/ 26 марта 2010

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

char trimmed[len];

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

1 голос
/ 26 марта 2010

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

1 голос
/ 26 марта 2010

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

0 голосов
/ 26 марта 2010

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

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

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

0 голосов
/ 26 марта 2010

Распределение и удаление вне функции:

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

Распределение внутри функции и удаление вне:

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

0 голосов
/ 26 марта 2010

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

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

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

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