当我声明一个指针时,为什么我必须使用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个字节。
push ebp
,mov ebp,esp
,sub esp,SIZEOF_LOCAL_VARS
),块的创建不会移动堆栈指针;我可能错了,所以请谨慎对待 ;) - Christoph需要注意的是,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”的代码。)
malloc()
从堆中分配内存,该内存明确为程序设置内存,并在程序运行时保持在范围内。因此,如果您不free()
内存,则将保留已分配的内存,并被视为内存泄漏。free()
并不取决于您是否声明了指针,而是取决于您是否使用malloc()
分配了内存。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#这样的新语言已经做到了这一点。