【问题标题】:Have I created a memory leak in this function?我是否在此函数中创建了内存泄漏?
【发布时间】:2021-09-17 04:33:50
【问题描述】:

努力提高我的 C 技能。这个程序应该接受一个字符串并去掉它的空格。

我知道我已经给malloc 打了两次电话,但只打了一次free。我是否因此在某处获得了一些未释放的内存,或者因为我将原始指针分配给了新内存,它是否在我所做的一次 free 调用中消失了?

谢谢!

static void foo() {
    char *string_to_change = malloc(sizeof(char) * 256);
    strcpy(string_to_change, "my test");
    printf("Before: %s\n", string_to_change); // "my test"
    strip_whitespace(&string_to_change);    
    printf("After: %s\n", string_to_change); // "mytest"
    free(string_to_change);
}

static void strip_whitespace(char **ptr) {
    
    char *res = malloc(strlen(*ptr));
    if (res == NULL)
        exit_with_error(NULL_POINTER);
    int current_pos = 0;

    for (int i = 0; i < strlen(*ptr); i++) {
        if (((*ptr)[i] == ' ') || ((*ptr)[i] == '\n' && ((*ptr)[i-1] == '\n' || (*ptr)[i+1] == '\0'))) {
            continue;
        }
        res[current_pos] = (*ptr)[i];
        current_pos++; 
    }
    res[current_pos] = '\0';
    *ptr = res;
}

【问题讨论】:

  • 是的,存在泄漏,因为您没有free foo 中分配的原始内存。
  • 是的,原来的string_to_change 丢失了。在最坏的情况下,您的 res 是 1 个元素太短(没有空格)。
  • malloc(和类似函数)返回的每个指针都需要在某个时候通过调用free 来释放,否则会造成内存泄漏。这些函数没有任何魔法。
  • @Barmar 字符串在 C 中以 null 结尾。这是我们在本网站上获得的最常见的 C 常见问题解答之一。
  • @HenryCooper free(*ptr); *ptr = res; 之前

标签: c pointers memory-management free


【解决方案1】:

KISS principle 表示我们应该编写尽可能简单的代码,而不是尽可能复杂。那就是:

  • 不要将分配和算法混为一谈。尽可能使用调用方分配。
  • 除非有明显的需要,否则不要使用动态分配。
  • 通常最好将字符串视为“不可变”,除非有特定要求,否则不要对它们进行就地修改。
  • 避免指针对指针和指针对指针的怪异算法。
  • continue 在 C 程序中的存在几乎完全肯定表明存在不必要的复杂循环。
  • 不要重新发明轮子。有ctype.h。

如果我们要通过调用者分配来实现这一点,代码可以归结为紧凑且易于阅读的内容,例如:

void strip_spaces (char* dst, const char* src)
{
  while(*src != '\0')
  {
    if(!isspace(*src))
    {
      *dst = *src;
      dst++;
    }
    src++;
  }
  *dst = '\0';
}

完整的程序:

#include <ctype.h>
#include <stdio.h>

void strip_spaces (char* dst, const char* src)
{
  while(*src != '\0')
  {
    if(!isspace(*src)) // if not string
    {
      *dst = *src; // then copy
      dst++;
    }
    src++;
  }
  *dst = '\0';
}

int main (void)
{
  char str[] = "   weird  \n\r string contain    ing    spac\tes\n";
  char stripped[128];
  
  strip_spaces(stripped, str);
  puts(stripped);
}

【讨论】:

  • 虽然我总体上同意您的 cmets,但我认为“继续”可能是写一些东西的最简单和最清晰的方式。例如,遍历包含一些异常的大量事物:for (...) { if (exception) continue; ... }
  • @LeeDanielCrocker 给我任何带有 continue 的循环,我相当有信心可以用更易读的方式编写它。无论如何,这是另一个问题的主题。
【解决方案2】:

确实,您的代码中存在内存泄漏。

假设您第一次调用malloc 在地址0x0010 分配内存,第二次调用在地址0x0100 分配内存。您的 free 调用将有效地释放地址 string_to_change 处的内存,即 0x0100 但没有告诉编译器释放 0x0010 处的内存。

通常,free 的调用次数必须与malloc 的调用次数一样多。

【讨论】:

    【解决方案3】:

    你这里有泄漏:

    *ptr = res;
    

    在此行之前,*ptr 指向已分配的内存块。然后将另一个已分配块的起始地址分配给它,这意味着原始指针丢失了。您应该在此行之前free:

    free(*ptr);
    *ptr = res;
    

    此外,这种分配可能太少:

    char *res = malloc(strlen(*ptr));
    

    如果*ptr 不包含要删除的空格,res 将没有足够的内存来保存终止的空字节。您需要为此添加 1。

    char *res = malloc(strlen(*ptr) + 1);
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2021-12-16
      • 1970-01-01
      • 1970-01-01
      • 2011-05-16
      • 1970-01-01
      • 2016-02-04
      • 1970-01-01
      相关资源
      最近更新 更多