strdup()在free()之后仍存在内存泄漏

7
我从未需要使用strdup(stringp)strsep(&stringp_copy, token)一起,直到最近我发现这可能会导致内存泄漏。

(strdup()以前总是可以正常free的。)

我已经修复了内存泄漏问题,并且我认为我理解了如何修复,但我无法弄清楚为什么我需要这样做。

原始代码(摘要):

const char *message = "From: username\nMessage: basic message\n";
char *message_copy, *line, *field_name;
int colon_position;
message_copy = strdup(message);

while(line = strsep(&message_copy, "\n")) {
  printf(line);
  char *colon = strchr(line, ':');
  if (colon != NULL) {
    colon_position = colon - line;
    strncpy(field_name, line, colon_position);
    printf("%s\n", field_name);
  }
}

free(message_copy);

新的不泄漏内存的代码:
const char *message = "From: username\nMessage: basic message\n";
char *message_copy, *freeable_message_copy, *line, *field_name;
int colon_position;
freeable_message_copy = message_copy = strdup(message);

while(line = strsep(&message_copy, "\n")) {
  printf(line);
  char *colon = strchr(line, ':');
  if (colon != NULL) {
    colon_position = colon - line;
    strncpy(field_name, line, colon_position);
    printf("%s\n", field_name);
  }
}

free(freeable_message_copy);

在第一段代码中,message_copy指针是如何被覆盖的?还是没有被覆盖?


3
不要使用printf(line);如果line包含类似于printf()格式字符串转换说明符,它将会读取或者写入错误的内存。可以使用fputs(line, stdout) 或者 printf("%s", line),它们是安全的且等效的。 - Jonathan Leffler
2
message_copystrsep 覆盖了。请阅读手册。 - Marian
谢谢,@JonathanLeffler。printf(line) 是为了快速而简单的调试。 - oliverseal
4个回答

11

函数strsep()接受指向原始字符串(message_copy)的指针,并修改它以返回指向“下一个”标记的新指针

const char *message = "From: username\nMessage: basic message\n";
char *message_copy, *original_copy;
//here you have allocated new memory, a duplicate of message
message_copy = original_copy = strdup(message);

在这里打印出指针。

printf("copy %p, original %p\n", message_copy, original_copy);

请注意,使用strsep()函数时会修改message_copy的内容。

char* token;
//here you modify message_copy
while(token = strsep(&message_copy, "\n")) {
    printf("%s\n", token);
}

这说明了message_copy已经改变,而original_copy保持不变。

printf("copy %p, original %p\n", message_copy, original_copy);

由于message_copy并不指向原始的strdup()结果,因此这样做是错误的。

free(message_copy);

但是保留原始的strdup()结果,并且这个free能够正常工作。

//original_copy points to the results of strdup
free(original_copy);

我完全没有注意到strsep手册中修改了指针。 - oliverseal

6
由于 strsep() 修改了 message_copy 参数,因此您试图释放未由 malloc() 等返回的指针。这会引起一些 malloc() 库和 valgrind 的投诉。这也是未定义的行为,通常会导致不久之后崩溃(但在与造成损坏的代码无关的不便位置的代码中崩溃)。
实际上,您的循环会迭代直到 message_copy 被设置为 NULL,因此您正在释放 NULL,这是被定义和安全的行为,但它也是一个无操作。它没有释放通过 strdup() 分配的指针。
总结:
  • 仅释放内存分配器返回的指针。
  • 不要释放内存分配器返回的块中间或末尾的指针。

1

请阅读 strsep 的手册这里

简单来说,strsep 函数会修改传入函数的原始字符指针,用 \0 覆盖每个分隔符的出现,并更新原始字符指针以指向 \0 后面的位置。

你的第二个版本没有泄漏,因为你创建了一个临时指针,指向从 strdup() 返回的原始 char 指针的开头,所以内存得到正确释放,因为你使用 free() 调用了被 strsep() 修改过的原始 char 指针而不是更新后的指针。


0
从man页中,
...此令牌通过用空字节('\0')覆盖分隔符来终止,并且*stringp被更新为指向令牌之后的位置....

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接