为什么我在声明指针时必须使用 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; 在堆栈上创建一个变量。
指针:
只是为了澄清:指针变量是在堆栈上创建的,它们保存在堆上分配的数据的内存地址。它们在 32 位系统上占用堆栈上的 4 个字节,在 64 位系统上占用堆栈上的 8 个字节。
应该注意的是,C 没有堆栈或堆的概念,尽管前面的答案在大约 99% 的情况下是正确的,并且给出了很好的见解。
C 定义了对象的三个存储持续时间:静态、自动和分配。(§6.2.4.1)
静态对象(例如全局变量)在整个程序期间都可用。
只要变量在范围内,自动对象就存在。一旦超出范围,它们就会不复存在。
请注意,这是两个极端。C 给你一个介于两者之间的点:分配的对象。(搜索词将动态分配内存。)通过这些,您可以告诉计算机对象应该何时开始和结束它们的存在。这是通过使用标准函数malloc()(或衍生物)和free()来完成的。
严格来说,您不必调用free ()。或者你可能会这样做(你必须阅读标准以获得权威点),但你可以在程序终止之前在main()结束时完成所有操作。或者,将它留给操作系统为您完成(如果不是全部的话,大多数情况下都会这样做)。但这又是一个极端——当您调用malloc()时对象就会存在,当您的程序终止时就会消失。
我不必在这里详细讨论实际含义:内存是有限的。在内存不足之前,您只能分配这么多字节。对所有东西都使用静态对象太浪费了;尝试重用大块或一大块静态内存将是困难的,并且在任何情况下都类似于动态分配方法。对长寿命对象使用自动存储将迫使您使其范围尽可能大,无论如何大致对应于静态对象的范围。
--
现在,一些注意事项:
{
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”编写更多代码。)
阿尔尼塔克是正确的。我想指出“在堆栈上”的真正含义。
当程序调用函数时,它希望函数完成一些工作,然后返回并继续下一行代码。当函数完成时,函数无法知道返回到哪里。因此,机器对每个程序都有一个调用栈,用于在调用函数之前将程序中以下语句的地址压入。“return”语句只是弹出程序地址并跳转到它。
堆栈也是一个方便的临时空间便笺簿。可以写入堆栈的未使用区域。在 C 函数中声明一个局部变量就是这样做的。当函数返回时,堆栈不需要被清理、释放或以其他方式处理,因为它只是一个临时空间,现在超出了范围。
相反,调用malloc()
从堆中分配内存,堆显式地为程序留出内存并在程序运行期间保持在范围内。因此,如果您没有free()
内存,它将保持分配状态并被视为内存泄漏。
需要free()
不取决于您是否声明了指针,而是取决于您是否已malloc()
编辑内存。
正如 Brian Bondy 之前所说,变量(“ int number
”、“ char string[10]
”、“ float your_boat
”等)在超出范围时消失,例如当您的代码离开功能块时。因此,您的问题中的指针(“ temp
”)在您调用时不会消失free()
- 相反,无论您在调用时分配的代码malloc()
都会消失。你的指针仍然停留在那里,即在你的示例代码之后你可以说“ temp = &some_other_variable
”而不必(再次)说“ int *temp;
”。
如果有人实现了一个函数,他们也碰巧调用了这个函数,malloc()
它会为你的程序申请内存,并且不需要你释放那个数据,那么你可以说
int * temp = (int*)malloc(sizeof(int));
后话不说
free(temp);
但这不是malloc()
实施的方式。
进一步指出,这篇文章进一步澄清了事情。
这是一个很好的问题,虽然很多人会回答这是堆栈分配和堆分配之间的区别,但基本的答案是底层系统向您暴露了一些不应该的东西。
当您分配内存时,您不必担心将其归还。系统应该足够聪明,可以确定您不再可以访问(指向或引用)它,因此它可以自动收回内存。
Java 和 C# 等较新的语言已经做到了这一点。