C 在不同功能中分配的可用内存?

C 在不同功能中分配的可用内存?,c,pointers,memory-management,malloc,free,C,Pointers,Memory Management,Malloc,Free,我正在尝试学习C语言,目前我正在尝试编写一个基本的堆栈数据结构,但我似乎无法正确使用基本的malloc/free 下面是我一直在使用的代码(我只是在这里发布一小部分来说明一个特定的问题,不是全部代码,而是通过在valgrind中运行此示例代码生成的错误消息) 我认为这个错误意味着不允许destroyEntry函数修改main中显式分配的内存。是这样吗?为什么我不能在另一个函数中释放在main中分配的内存?(这种行为在某种程度上特定于main吗?每当您将参数传递给函数时,就会生成一个副本,并且函数

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

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


我认为这个错误意味着不允许
destroyEntry
函数修改main中显式分配的内存。是这样吗?为什么我不能在另一个函数中
释放
main
中分配的内存?(这种行为在某种程度上特定于main吗?

每当您将参数传递给函数时,就会生成一个副本,并且函数会在该副本上工作。因此,在您的情况下,您试图
释放
原始对象的副本,这没有任何意义


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

这是通过值传递的,这意味着创建了副本,因此您尝试释放本地变量
条目
所在的内存。请注意,
entry
是一个具有自动存储持续时间的对象,当您的程序超出
destroyEntry
功能的范围时,它所在的内存将自动释放

void destroyEntry(Entry entry)
{
    Entry *entry_ptr = &entry;
    free(entry_ptr);
    return;
}
函数应采用指针(通过引用传递):


然后,而不是
destroyEntry(*(apple))你只需调用
销毁条目(苹果)。请注意,如果没有其他与
destroyEntry
功能相关的功能,这是多余的,最好直接调用
free(apple)

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

即使你知道了你的问题,回到错误并尝试将你所看到的文本与问题联系起来也是有帮助的,这样下次出现问题时,你可能会更快地找到答案。我发现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上发布您的问题。数百名程序员可能会从您这样做中受益。

+1用于明确问题和SSCCE。@MatteoItalia我以前从未听说过。这绝对是个好主意。谢谢你向我介绍它。谢谢你发布这篇文章…这是一个关于用指针等找出错误的非常好的观点。我一直在学习堆栈和堆,但没有意识到它们有通用的内存地址。(所以,
NULL
-->
0x0
stack
-->
0x4
和heap是什么?)此外,我不能对你的帖子进行一个字符的更改,但是在printf字符串中:
苹果的地址=%d”
格式字符串应该是
%p
,而不是
%d
,对吗?我不能百分之百肯定地告诉你堆地址总是“0x8…”,堆栈地址总是“0x4…”。老实说,我现在主要做嵌入式编程,地址空间定义得很好,只有你的进程在运行。然而,我的理解是,在x86上,每个进程都有自己的虚拟地址空间,根据我的经验,这些“规则”似乎是正确的。感谢您指出%d问题。它可以工作(因为它可以打印地址),但更难阅读。我自己通常使用“0x%x”,因为这是我习惯于看到它的方式。因此,为了清楚起见,将
*(apple)
传递到
DestroyEntry
函数中会创建一个新的
Entry
结构,它与原始的
apple
结构分离?
void destroyEntry(Entry *entry)
{
    free(entry);
}
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
}