Утечка памяти из-за отсутствия free () в этой функции - PullRequest
0 голосов
/ 28 февраля 2019

Следующий код используется для поиска пути исполняемого файла для элементарной оболочки в c.Как видите, я динамически распределяю переменную path, а затем проверяю, существует ли путь (через lstat в вспомогательной функции cmd_exists).Затем я возвращаю переменную пути.Моя проблема в том, что это вызывает утечку памяти, так как path никогда не освобождается.Я не могу освободить путь до того, как верну его значение, и на данный момент я не могу придумать способ освободить выделенную память.Если кто-нибудь может мне помочь, я буду признателен.Спасибо

char * find_path(char * mypath, char * command){
     char * token = strtok(mypath, "#");
     while(token != NULL){
                         /*token size + 1 (for /) + command size*/
          char * path = calloc(strlen(token)+1+strlen(command)+1, sizeof(char));
          strcat(path, token);
          strcat(path, "/");
          strcat(path, command);
          if(cmd_exists(path) == 1){
             return path;
          }
          token = strtok(NULL, "#");
     }
     return NULL;
}

Ответы [ 3 ]

0 голосов
/ 28 февраля 2019

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

Я вижу 3 решения, как решить эту проблему:

  • Худшее решение 3 - иметь глобальный статический буфер и позволить вашей функции find_path заполнить этот буфер и вернуть указатель на него вызывающей стороне.Этот прием используется довольно многими другими стандартными функциями C, но с ним также связано много проблем (часто они не поточнобезопасны, а если они поточнобезопасны, то следующий вызов может перезаписать предыдущее возвращаемое значение).См. https://en.cppreference.com/w/cpp/utility/program/getenv для функции, которая имеет такое поведение (см. Предупреждения наверху).
  • Несколько лучшим решением является документирование возвращаемого значения вашей функции и четкое сообщение вызывающей стороне, что это ЕГОответственность за освобождение возвращенного указателя.Если он не получит утечку памяти.См. https://en.cppreference.com/w/c/experimental/dynamic/strdup для функции, которая использует это поведение.
  • Другое решение состоит в том, чтобы вызывающая сторона передавала буфер вашей функции с максимальным размером.Таким образом, вместо возврата char * вы добавляете аргументы char * и size_t (size_t, указывающий размер буфера char * в символах).Тогда ваша функция find_path может заполнить этот буфер.Смотрите https://en.cppreference.com/w/cpp/string/byte/strncpy для функции с таким поведением.Проблема с этим подходом состоит в том, что если буфер недостаточно велик, ваша функция должна вернуть ошибку, и вызывающая сторона должна передать больший буфер.Некоторые функции Windows решают эту проблему с помощью функции, возвращающей «ожидаемый» размер буфера, поэтому, если вызов не удался (поскольку буфер недостаточно велик), вызывающая сторона может использовать возвращаемое значение, чтобы увидеть, насколько большим должен быть буфер, и выделитьбольший буфер.

Мое предпочтительное решение зависит от конкретного случая.Я бы взял третий вариант, если есть значимый максимальный размер буфера (например, максимальный путь к файлу).Я бы взял второй вариант - максимальный размер буфера предсказать сложно.В любом случае я бы никогда не использовал первый вариант.

0 голосов
/ 28 февраля 2019

Вам нужно иметь free в двух возможных местах.

  1. Вам нужно освободить путь, если cmd_exists вернет false.
  2. Вам нужно иметь свободный метод вызоваесли cmd_exists возвращает true.

1.

 while(token != NULL){
                     /*token size + 1 (for /) + command size*/
      char * path = calloc(strlen(token)+1+strlen(command)+1, sizeof(char));
      strcat(path, token);
      strcat(path, "/");
      strcat(path, command);
      if(cmd_exists(path) == 1){
         return path;
      }

      free(path);   // 1. Here

      token = strtok(NULL, "#");
 }
 return NULL;

2.

 char *temp = find_path(...);
  .....//do your stuff
  if (temp) free(temp);
0 голосов
/ 28 февраля 2019

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

// in caller
char * s = find_path("/mypath", "command");
// do something about `s`
free(s);
...