67

我正在尝试学习 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?)

4

3 回答 3

52

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

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

于 2012-06-17T12:14:41.043 回答
38

这是按值传递,这意味着创建了副本,因此您尝试释放局部变量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);. 注意,如果没有与function相关的其他destroyEntry功能,那是多余的,最好直接调用free(apple)

于 2012-06-17T12:16:04.263 回答
10

这里的其他答案指出了主要问题——因为当你在 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() 分配的堆上,但我假设您刚刚开始那是对你来说还不是危险信号),所以你知道你正试图从错误的地方释放内存,因此“无效的 free()”。

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

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

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

于 2012-06-20T15:39:44.037 回答