此函数中缺少 free() 导致内存泄漏

Memory leak from lack of free() in this function

以下代码用于在 c 中查找基本 shell 的可执行文件的路径。如您所见,我动态分配 path 变量,然后检查路径是否存在(通过辅助函数 cmd_exists 中的 lstat)。然后我 return 路径变量。我的问题是,这会导致内存泄漏,因为 path 永远不会被释放。在我 return 它的值之前我无法释放路径,截至目前我想不出任何方法来释放已分配的内存。如果有人可以帮助我,我将不胜感激。谢谢

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;
}

您可以要求调用方释放内存:

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

这确实是 C 的问题之一。内存所有权可能非常困难,因为 C++ 中没有 RAII 概念(本质上,没有自动析构函数)。

我看到 3 个解决方案:

  • 3 中最糟糕的解决方案是拥有一个全局静态缓冲区,让您的 find_path 函数填充此缓冲区,并 return 将指向它的指针指向调用者。这个技巧被很多其他标准的 C 函数使用,但是它也有很多问题(通常不是线程安全的,如果它是线程安全的,那么下一次调用可能会覆盖之前的 return 值).请参阅 https://en.cppreference.com/w/cpp/utility/program/getenv 了解具有此行为的函数(查看顶部的警告)。
  • 稍微好一点的解决方案是记录函数的 return 值,并清楚地告诉调用者释放 returned 指针是他的责任。如果他不这样做,你就会发生内存泄漏。有关使用此行为的函数,请参阅 https://en.cppreference.com/w/c/experimental/dynamic/strdup
  • 另一种解决方案是让调用者将最大大小的缓冲区传递给您的函数。因此,不是 returning char *,而是添加 char *size_t 参数(size_t 以字符表示 char * 缓冲区的大小) .然后你的 find_path 函数可以填充这个缓冲区。有关具有此行为的函数,请参阅 https://en.cppreference.com/w/cpp/string/byte/strncpy。这种方法的问题是,如果缓冲区不够大,您的函数需要 return 失败,调用者必须传递更大的缓冲区。一些 Windows 函数通过让函数 return 设置 'expected' 缓冲区大小来解决这个问题,因此如果调用失败(因为缓冲区不够大),调用者可以使用 return 值来查看缓冲区应该有多大,并分配一个更大的缓冲区。

我的首选解决方案取决于实际情况。如果存在有意义的最大缓冲区大小(例如最大文件路径),我会采用第三种选择。我会采取第二种选择是最大缓冲区大小很难预测。无论如何,我永远不会使用第一种选择。

您需要在两个可能的地方使用 free

  1. 如果 cmd_exists returns false,您需要释放路径。
  2. 如果 cmd_exists returns 为真,您需要在调用方方法中有 free。

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);