【问题标题】:free() function only frees the first element of a struct?free() 函数只释放结构的第一个元素?
【发布时间】:2019-07-19 08:31:59
【问题描述】:

我正在使用两个结构:

typedef struct ListNode{
    void* value;
    struct ListNode *next;
    struct ListNode *prev;
} Node;

typedef struct List{
    Node *first;
    Node *last;
    int count;
} List;

然后我声明这些变量并分配内存

List *x = calloc(1,sizeof(List));
Node *x1 = malloc(sizeof(Node));
Node *x2 = malloc(sizeof(Node));
x->first = x1;
x->last =x2;
x->count = 2;
free(x);

所以我很确定如果我释放xx->countx->firstx->last 也会消失。但是,在这种情况下,我使用 gdb 进行检查,令人惊讶的是,只删除了 x 的第一个元素(例如:如果在 struct List 中我将 count 放在顶部,那么它将只删除 count 而其余部分保持不变)。为什么会发生这种情况,我如何 free() 整个结构?

【问题讨论】:

  • “所以我很确定如果我释放 x,x->count,x->first,x->last 也会消失。”整个对象被释放,因此,作为对象一部分的指针first 消失了。但是,指针指向的对象却没有。为此,您必须事先致电free(x->first);
  • 我知道我的例子是不完整的,但这里的重点是指针“last”并没有像“count”一样消失。
  • 指针lastcount 整数一样消失。无论如何,存储到该内存地址的内容仍将在内存中,除非您在某处保存了指针的副本,否则无法访问。
  • “消失”是什么意思? memoys 位置仍然存在。内存单元不会从您的硬件中移除。 ;) 值也没有改变。调用 free 后,您根本不允许访问任何这些内存位置

标签: c memory-leaks malloc free


【解决方案1】:

你的假设是错误的。如果您执行free(x),您只是释放了保存x 内容的内存块,但由于此块包含一些指向其他块的指针,您需要先free() .这是因为free() 不关心您正在释放的内存的内容,它不会做任何花哨的事情。如果您在使用 free() 后看到一些值被修改,那只是因为 free() 使用该内存来存储一些有用的内部值以跟踪内存使用情况,如果这恰好“清除”了您的指针结构。

free() 结构的正确方法如下:

free(x->first);
free(x->last);
// DO NOT free(x->count), it's an integer, not a previously allocated pointer!
free(x);

这是一个视觉表示(请原谅我糟糕的笔迹):

【讨论】:

  • 好图! ;)
  • 感谢您如此专注!所以在我调用 free(x) 之后我无法访问 x->first 对吗?
  • @VanTeoLe 没错。如果您 free() 某事,您将无法再访问它,因为您无法保证该内存块的内容了。
  • 等等,让我问一个愚蠢的问题。 x->first 是一个指针,也是结构的一部分。如果我释放(x),那么当我尝试访问 x->first 时,它会让我作为一个单纯的指针传递(因为指针仍然存在)还是不允许我作为结构的成员访问内存我解放了吗?
  • @VanTeoLe 在你释放 x 之后,你仍然可以做 x->first,但你不能保证这样的指针仍然有效。它可能是有效的,或者它可能已被free() 或其他东西触及。这是未定义的行为,您不能依赖它。实际上,如果您尝试使用地址清理程序 (-O0 -g -fsanitize=address -lasan -fno-omit-frame-pointer) 进行编译并运行执行 free(x); free(x->first); 的程序,您将收到一个巨大的错误,并且您的程序将被 asan 停止。
【解决方案2】:

了解结构的内存布局很重要...我不是专家,所以这将非常简化:

列表 x = [pointer-to-x1][pointer-to-x2][int](加上填充)

因此,当您运行 calloc(1, sizeof(List)) 时,您不会在与 List 相同的内存区域中创建 x1 和 x2 结构,因为它们只是指向内存中其他位置的指针。您只是在创建对它们的小引用,甚至还没有设置。这就是你必须分别 malloc x1 和 x2 的原因。

所以当你释放(x)时,你也没有释放 x1 或 x2。 free 不知道如何也不知道您为 x1 和 x2 运行了 malloc。如果它试图释放它,它最终可能会过早释放内存或释放堆栈上的内存。它只会查看我在上面绘制的内存区域。

正在写这个,看到另一个答案刚刚进来。Marco 有一个很好的答案!

【讨论】:

    【解决方案3】:

    您似乎对拨打free 时会发生什么没有预期。 您无法在 GDB 中验证 free 的结果。

    释放内存位置并不意味着该位置的内存以任何方式被修改。它可能看起来仍然和以前一样。 OS 或 C 运行时库可能会将其设置为零,但这不是强制性的。指针x 也不会改变。

    释放地址仅意味着您告诉操作系统它可以将其取回并提供给其他人。 调用free 后,您将不再被允许访问内存。 这就是free 之后发生的所有事情。

    因此lastcount 不会“消失”。

    也就是说,正如其他人已经指出的那样,您也存在内存泄漏。释放x 不会释放任何其他可通过x->firstx->last 访问的内存。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2022-12-04
      • 2014-04-07
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2015-12-29
      相关资源
      最近更新 更多