【发布时间】:2010-10-12 05:58:53
【问题描述】:
为什么我在声明指针时必须使用free()例如:
int *temp = (int*)malloc(sizeof(int))
*temp = 3;
但不是在我这样做的时候:
int temp = 3;
【问题讨论】:
为什么我在声明指针时必须使用free()例如:
int *temp = (int*)malloc(sizeof(int))
*temp = 3;
但不是在我这样做的时候:
int temp = 3;
【问题讨论】:
普通声明被放入堆栈。当函数返回时,堆栈指针恢复到函数调用前的值,因此内存被自动回收。
基于 Malloc 的声明是从“堆”分配的,这需要程序员管理分配和解除分配。
【讨论】:
您不必总是在指针上使用 free,只需使用 malloc 声明的指针即可。你可以声明一个指向栈上内存位置的指针
int a = 3;
int* p = &a;
当它超出范围时,这个内存(连同指针)也将被自动释放。使用 malloc 会在堆上分配相同的内存,因此您必须手动处理清理。
【讨论】:
因为语言让您在堆栈和堆之间进行选择。
为什么要在堆栈和堆之间进行选择的原因:
为什么不能自动释放堆:
因为没有办法知道你何时完成了记忆。有一些方法可以模拟垃圾收集,但这涉及到堆栈上没有更多变量并且堆上持有指向堆上数据的指针的情况。
更多关于堆栈与堆的信息:
C 语言让您选择是否要在堆栈或堆上定义变量。
malloc 在堆上创建变量。一个简单的声明,例如 int x;在堆栈上创建一个变量。
See further reading on stack vs heap in my answer here。
指针:
澄清一下:指针变量是在堆栈上创建的,它们保存在堆上分配的数据的内存地址。它们在 32 位系统上占用堆栈上的 4 个字节,在 64 位系统上占用堆栈上的 8 个字节。
【讨论】:
push ebp,mov ebp, esp,sub esp, SIZEOF_LOCAL_VARS)并且块创建不会移动堆栈指针;我可能是错的,所以把这个和一粒盐放在一起;)
应该注意的是,C 没有堆栈或堆的概念,尽管前面的答案在大约 99% 的情况下是正确的,并且提供了很好的洞察力。
C 为对象定义了三种存储持续时间:static、automatic和allocated。 (§6.2.4.1)
静态对象(例如全局变量)在整个程序期间都可用。
只要变量在范围内,自动对象就存在。一旦超出范围,它们就会不复存在。
请注意,这是两个极端。 C 给你一个介于两者之间的点:Allocated 对象。 (搜索词将是动态分配的内存。)通过这些,您告诉计算机对象应该何时开始和结束它们的存在。这是通过使用标准函数 malloc()(或衍生函数)和 free() 来完成的。
严格来说,您不必必须调用free()。或者也许你会这样做(你必须阅读标准以获得权威观点),但你可以在程序终止之前在 main() 的末尾完成所有操作。或者,让操作系统为你做这件事(如果不是全部的话,大多数人都会这样做。)但这又是一个极端——当你调用 malloc() 时,对象就会存在,当你的程序终止时出去。
我不应该在这里详细讨论实际含义:内存是有限的。在内存不足之前,您只能分配这么多字节。对所有东西都使用 static 对象太浪费了;尝试重用大块或一大块 static 内存会很困难,并且在任何情况下都类似于动态分配方法。对长寿命对象使用 自动 存储将迫使您将它们的范围尽可能大,无论如何大致对应于 静态 对象的范围。
--
现在,一些注意事项:
{
int *temp = malloc(sizeof(int));
*temp = 5;
//free(temp);
}
注意这里的temp 是一个自动对象。它只会在它的作用域内存活,它以 } 结尾。然而,它指向的对象 已被分配。它会一直存在,直到您在其地址上调用 free()。由于temp 包含该地址的唯一副本,因此一旦temp 超出范围,您将失去调用free() 的机会。一些内存将被永久分配,但不可用。这称为内存泄漏。
垃圾收集是另一种管理对象存储的方法。 C 中的实现可能如下所示:
{
int *temp = gc_malloc(sizeof(int));
*temp = 5;
}
在 } 处,计算机将确定对已分配对象的最后一个引用 temp 丢失了,最好释放它。
这是一种权衡,您不必担心释放对象(这不是一件小事,因为简单的示例可能会让您想到),但是这里的 gc_malloc() 比简单的 malloc(),并且在 temp 超出范围的 } 处执行了不可见的代码。计算机如何决定temp 是最后一个引用,这是一个完全不同的话题。 (一些实用的解决方案可能需要您围绕“int *temp”编写更多代码。)
【讨论】:
Alnitak 是正确的。我想指出“stack”的真正含义。
当程序调用函数时,它希望函数完成一些工作,然后返回并继续下一行代码。当函数完成时,函数无法知道返回到哪里。因此,机器对每个程序都有一个调用栈,用于在调用函数之前将程序中以下语句的地址压入。 “return”语句只是简单地弹出程序地址并跳转到它。
堆栈也是一个方便的临时空间便签本。可以写入堆栈的未使用区域。在 C 函数中声明一个局部变量就是这样做的。当函数返回时,堆栈不需要清理、释放或以其他方式处理,因为它只是一个临时空间,现在超出了范围。
相比之下,调用malloc() 会从堆中分配内存,这会显式地为程序留出内存并在程序运行期间一直保持在作用域内。因此,如果您不free() 内存,它将保持分配状态并被视为内存泄漏。
【讨论】:
free() 的需要并不取决于你是否声明了一个指针,而是你是否有malloc()ed 内存。
就像 Brian Bondy 之前所说的,变量(“int number”、“char string[10]”、“float your_boat”等)会在超出范围时消失,例如当您的代码离开功能块时。因此,当您调用 free() 时,您的问题中的指针 ("temp") 不会消失 - 相反,无论您在调用 malloc() 时分配的代码都消失了。您的指针仍然停留在那里,即在您的示例代码之后您可以立即说“temp = &some_other_variable”而不必(再次)说“int *temp;”。
如果有人实现了一个函数,他们也碰巧调用了malloc(),它会为你的程序申请内存,并且不需要你释放这些数据,那么你会会说
int * temp = (int*)malloc(sizeof(int));
事不宜迟
free(temp);
但这不是malloc() 的实现方式。
【讨论】:
进一步指出this post 进一步澄清了事情。
【讨论】:
这是一个很好的问题,虽然很多人会回答这是堆栈和堆分配之间的区别,但基本的答案是底层系统向您暴露了一些不应该的东西。
当您分配内存时,您不必担心将其归还。系统应该足够聪明,可以判断出您不再可以访问(指向或引用)它,因此它可以自动收回内存。
Java 和 C# 等较新的语言已经做到了这一点。
【讨论】: