我是否在此函数中创建了内存泄漏?

问题描述

努力提高我的 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;
}

解决方法

你这里有漏洞:

*ptr = res;

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

free(*ptr);
*ptr = res;

此外,此分配可能太少:

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

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

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

KISS principle 意味着我们应该编写尽可能简单的代码,而不是尽可能复杂。即:

  • 不要将分配和算法混为一团糟。如果可能,请使用调用者分配。
  • 除非明显需要,否则不要使用动态分配。
  • 通常最好将字符串视为“不可变”,除非有特定要求,否则不要对它们进行就地修改。
  • 避免指针对指针和对指针的奇怪算术。
  • C 程序中 continue 的存在几乎表明存在不必要的复杂循环。
  • 不要重新发明轮子。有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);
}
,

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

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

通常,您确实必须拥有与 free 次调用一样多的 malloc 次调用。