我是否在此函数中造成了内存泄漏?

Have I created a memory leak in this function?

正在努力提高我的 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] == '[=10=]'))) {
            continue;
        }
        res[current_pos] = (*ptr)[i];
        current_pos++; 
    }
    res[current_pos] = '[=10=]';
    *ptr = res;
}

你这里有漏洞:

*ptr = res;

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

free(*ptr);
*ptr = res;

此外,此分配可能太少:

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

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

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

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

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

通常,free 个调用确实与 malloc 个调用一样多。

KISS principle的意思是我们应该把代码写得越简单越好,而不是越复杂越好。即:

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

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

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

完整的程序:

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

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

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