【问题标题】:Free memory allocated in a different function?在不同的函数中分配的空闲内存?
【发布时间】:2012-06-19 17:37:57
【问题描述】:

我正在尝试学习 C,目前正在尝试编写一个基本的堆栈数据结构,但我似乎无法掌握基本的 malloc/free 正确。

这是我一直在使用的代码(我只是在这里发布一小部分来说明一个特定的问题,而不是全部代码,但错误消息是通过在valgrind中运行此示例代码生成的)

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

typedef struct Entry {
    struct Entry *previous;
    int value;
} Entry;

void destroyEntry(Entry entry);

int main(int argc, char *argv[])
{
    Entry* apple;
    apple = malloc(sizeof(Entry));
    destroyEntry(*(apple));
    return 0;
}

void destroyEntry(Entry entry)
{
    Entry *entry_ptr = &entry;
    free(entry_ptr);
    return;
}

当我通过valgrind--leak-check=full --track-origins=yes 运行它时,我收到以下错误:

==20674== Invalid free() / delete / delete[] / realloc()
==20674==    at 0x4028E58: free (vg_replace_malloc.c:427)
==20674==    by 0x80485B2: destroyEntry (testing.c:53)
==20674==    by 0x8048477: main (testing.c:26)
==20674==  Address 0xbecc0070 is on thread 1's stack

我认为这个错误意味着destroyEntry函数不允许修改在main中显式分配的内存。那正确吗?为什么我不能在另一个函数中只 free 我在 main 中分配的内存? (这种行为是否特定于 main?)

【问题讨论】:

  • +1 表示明确的问题和 SSCCE。
  • @MatteoItalia 我以前从未听说过SSCCE。绝对是个好概念。感谢您向我介绍它。

标签: c pointers memory-management malloc free


【解决方案1】:

每当您将参数传递给函数时,都会创建一个副本,并且该函数在该副本上工作。因此,在您的情况下,您正在尝试 free 原始对象的副本,这没有任何意义。

你应该修改你的函数来获取一个指针,然后你可以让它直接在那个指针上调用free

【讨论】:

  • 所以为了清楚起见,将*(apple) 传递给DestroyEntry 函数会创建一个新的Entry 结构,它与原来的apple 是分开的?
【解决方案2】:

这是按值传递,这意味着创建了副本,因此您尝试释放局部变量entry 所在的内存。请注意,entry 是一个具有自动存储期限的对象,当您的程序超出 destroyEntry 函数的范围时,它所在的内存将被自动释放。

void destroyEntry(Entry entry)
{
    Entry *entry_ptr = &entry;
    free(entry_ptr);
    return;
}

你的函数应该接受一个指针(通过引用传递):

void destroyEntry(Entry *entry)
{
    free(entry);
}

然后你只需调用destroyEntry(apple); 而不是destroyEntry(*(apple));。请注意,如果没有与destroyEntry 函数相关的其他功能,则它是多余的,最好直接调用free(apple)

【讨论】:

    【解决方案3】:

    这里的其他答案指出了主要问题——因为你在 main() 中调用 destroyEntry 时取消引用你的苹果,它通过引用传递,创建一个副本。

    即使您知道自己的问题,也有助于回到错误并尝试将您所看到的文本与问题联系起来,这样下次出现问题时您可能更有可能发现它快出来。我发现 C 和 C++ 错误有时看起来非常模棱两可。

    一般来说,当我在释放指针或删除对象时遇到问题时,我喜欢打印出地址,尤其是在我分配地址和尝试释放它的时候。 valgrind 已经给了你坏指针的地址,但它有助于将它与一个好的指针进行比较。

    int main()
    {
      Entry * apple;
      apple = malloc(sizeof(Entry));
      printf("apple's address = %p", apple);  // Prints the address of 'apple'
      free(apple);   // You know this will work
    }
    

    这样做之后,您会注意到 printf() 语句为您提供了一个类似于 0x8024712 的地址(只是在正确的一般范围内构成一个地址),但您的 valgrind 输出给出了 0x4028E58。您会注意到它们位于两个非常不同的位置(实际上,“0x4 ...”在堆栈上,而不是 malloc() 分配的堆上,但我假设您刚刚开始那是对你来说还不是一个危险信号),所以你知道你正在尝试从错误的地方释放内存,因此“invalid free()”。

    因此,您可以从那里对自己说:“好吧,不知何故,我的指针被损坏了。”您已经将您的问题归结为一个可编译的小示例,因此您很快就可以从那里解决它。

    TL;DR - 当遇到与指针相关的错误时,请尝试打印地址或在您最喜欢的调试器中查找它们。它通常至少会为您指明正确的方向。

    当然,这并不是阻止您在 Stack Exchange 上发布您的问题。数以百计的程序员可能会从您这样做中受益。

    【讨论】:

    • 感谢您发布此内容...关于使用指针等找出错误,这是一个非常好的观点。我一直在学习堆栈与堆,但没有意识到它们具有通用的内存地址。 (所以,NULL --> 0x0stack-->0x4 和堆是什么?)。
    • 另外,我不能对您的帖子进行一个字符的更改,但是在 printf 字符串中:"apple's address = %d" 格式字符串应该是%p,而不是%d,对吗?
    • 我无法 100% 肯定地告诉您堆地址将始终为“0x8...”而堆栈地址将始终为“0x4...”。老实说,这些天我主要进行嵌入式编程,其中地址空间定义得非常好,并且您的进程是唯一运行的进程。但是,我的理解是,在 x86 上,每个进程都有自己的虚拟地址空间,根据我的经验,这些“规则”似乎是正确的。感谢您指出 %d 问题。它可以工作(因为它确实打印了地址),但它更难阅读。我自己通常使用“0x%x”,因为这就是我习惯看到的方式。
    猜你喜欢
    • 2015-03-21
    • 2011-07-13
    • 2014-11-24
    • 2014-11-27
    • 2018-10-09
    • 2013-05-02
    • 1970-01-01
    • 2011-03-27
    • 2011-07-26
    相关资源
    最近更新 更多