在使用strdup()函数后如何释放内存?C语言相关的IT技术问题。

17

我正在学习《零基础学C编程》这个在线课程。在下面的代码示例中,我不明白为什么需要两个free()调用。我认为只需要调用free()一次,因为只有一个malloc()。请问为什么我们需要两个?

如果我注释掉free(who->name);,那么valgrind会告诉我我失去了一块内存,就像这样:

LEAK SUMMARY:
definitely lost: 21 bytes in 2 blocks

以下是代码:

#include <stdio.h>
#include <assert.h>
#include <stdlib.h>
#include <string.h>

struct Person {
    char *name;
    int age;
    int height;
    int weight;
};

struct Person *Person_create(char *name, int age, int height, int weight)
{
    struct Person *who = malloc(sizeof(struct Person));
    assert(who != NULL);

    who->name = strdup(name);
    who->age = age;
    who->height = height;
    who->weight = weight;

    return who;
}

void Person_destroy(struct Person *who)
{
    assert(who != NULL);

    free(who->name); /* Why this one??! */
    free(who);
}

int main(int argc, char *argv[])
{
    // make two people structures
    struct Person *joe = Person_create(
            "Joe Alex", 32, 64, 140);

    struct Person *frank = Person_create(
            "Frank Blank", 20, 72, 180);

    // destroy them both so we clean up
    Person_destroy(joe);
    Person_destroy(frank);

    return 0;
}

6
因为strdup()也会分配内存,所以必须将其释放(参见文档)。 - Adriano Repetti
4
"strdup" 就像是伪装的 "malloc". - AnT stands with Russia
4
阅读标准函数文档比在SO上提问更快 - Basile Starynkevitch
5
strdup不是标准C函数,但它在POSIX中。除了malloccallocrealloc之外,没有标准C函数会返回需要释放的指针。 - M.M
3
标准函数可以在C99之外的其他标准中提及,Posix就是这样一个标准。 - Basile Starynkevitch
显示剩余6条评论
1个回答

29

strdup(3)被记录为

  The strdup() function returns a pointer to a new string which is a
  duplicate of the string s.  Memory for the new string is obtained
  with malloc(3), and can be freed with free(3).

顺便提一下,正如Matt McNabb所评论的那样,strdup是Posix标准中的,而不是C99语言规范中的。

当然,free只释放你传递给它的内存区域(它不会神奇地间接释放任何指向你传递给它的内存区域内部的区域)。再次引用free(3)的说法:

  The free() function frees the memory space pointed to by ptr, which
  must have been returned by a previous call to malloc(), calloc() or
  realloc().  Otherwise, or if free(ptr) has already been called
  before, undefined behavior occurs.  If ptr is NULL, no operation is
  performed.

阅读更多关于 C动态内存分配 的内容。如果您不喜欢这个,可以学习 垃圾回收。对于Linux和其他一些系统上的C语言,您可以考虑使用Boehm的保守垃圾收集器。然后您将使用GC_MALLOC和/或GC_MALLOC_ATOMIC代替malloc,并使用GC_STRDUP代替strdup,而不必担心free(如果需要,有时可能会使用GC_FREE)。我发现它非常有用,但也有一些缺点(比malloc慢一些,并且没有明确保证释放内存...)。

了解内存损坏内存泄漏

顺便提一下,您应该先使用所有警告和调试信息编译程序(例如gcc -Wall -g)。然后,您可以使用调试器(gdb),在main被执行后,在malloc中设置断点,并查看何时调用了malloc。您将看到strdup正在调用malloc....


FYI,在Linux上,malloc使用mmap(2)实现 -有时还会使用旧的sbrk(2)系统调用- 获取“大”内存区域(几千字节甚至兆字节),free有时可能会调用munmap(2) -对于这些大区域- 但通常只是将释放的块标记为可重用,以便在将来的某些调用中可以重新使用该块。因此,执行mallocfree的程序可能不会将所有先前使用的内存释放给内核。另请参阅关于内存碎片化的此问题

同时使用Valgrind地址泄漏检查工具

在一些操作系统(如Linux)上,您可以使用GCC编译器编译C代码,使用gcc -Wall -Wextra -g命令,在运行时使用valgrind工具。虽然会减慢执行速度,但它有助于发现像一些内存泄漏和一些缓冲区溢出这样的错误。如果您使用最新的GCC或Clang编译器,则还可以在编译时使用其地址泄漏检查工具(对生成的代码进行插桩)。

你可能还可以阅读与内存分配和垃圾回收算法相关的GC手册

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