12

为什么我在声明指针时必须使用 free(),例如:

int *temp = (int*)malloc(sizeof(int))
*temp = 3;

但不是当我这样做时:

int temp = 3;
4

8 回答 8

30

普通声明放在堆栈上。当函数返回时,堆栈指针恢复到调用函数之前的值,因此内存被自动回收。

基于 Malloc 的声明是从“堆”分配的,这需要程序员管理分配和释放。

于 2009-03-09T17:25:24.893 回答
12

您不必总是在指针上使用 free,只需使用 malloc 声明的指针。您可以声明一个指向堆栈上的内存位置的指针

int a = 3;
int* p = &a;

当它超出范围时,这个内存(连同指针)也将被自动释放。使用 malloc 会在堆上分配相同的内存,因此您必须手动处理清理。

于 2009-03-09T17:29:33.637 回答
8

因为该语言让您在堆栈和堆之间进行选择。

您要在堆栈和堆之间进行选择的原因:

  • 堆上的变量故意不释放自己,以便您可以在代码块或函数的范围之外使用它们。
  • 与堆相比,使用堆栈更有效
  • 在大多数编译器上,您无法在运行时选择堆栈上的对象或数组的大小,因此此处将使用堆。

为什么堆不能自动释放:

因为没有办法知道你何时完成了记忆。有很多方法可以模拟垃圾收集,但这涉及到堆栈和堆中没有更多变量时持有指向堆上数据的指针。

有关堆栈与堆的更多信息:

C 语言让您选择是否要在堆栈或堆上定义变量。

  • 堆栈上的变量在超出范围时会自动释放。
  • 堆上的变量不会自动释放。

malloc 在堆上创建变量。一个简单的声明,例如 int x; 在堆栈上创建一个变量。

在我的回答中查看关于堆栈与堆的进一步阅读

指针:

只是为了澄清:指针变量是在堆栈上创建的,它们保存在堆上分配的数据的内存地址。它们在 32 位系统上占用堆栈上的 4 个字节,在 64 位系统上占用堆栈上的 8 个字节。

于 2009-03-09T17:30:56.533 回答
5

应该注意的是,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”编写更多代码。)

于 2009-03-09T18:59:58.130 回答
3

阿尔尼塔克是正确的。我想指出“在堆栈上”的真正含义。

当程序调用函数时,它希望函数完成一些工作,然后返回并继续下一行代码。当函数完成时,函数无法知道返回到哪里。因此,机器对每个程序都有一个调用栈,用于在调用函数之前将程序中以下语句的地址压入。“return”语句只是弹出程序地址并跳转到它。

堆栈也是一个方便的临时空间便笺簿。可以写入堆栈的未使用区域。在 C 函数中声明一个局部变量就是这样做的。当函数返回时,堆栈不需要被清理、释放或以其他方式处理,因为它只是一个临时空间,现在超出了范围。

相反,调用malloc()从堆中分配内存,堆显式地为程序留出内存并在程序运行期间保持在范围内。因此,如果您没有free()内存,它将保持分配状态并被视为内存泄漏。

于 2009-03-09T17:39:25.767 回答
2

需要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()实施的方式。

于 2009-03-09T17:49:12.310 回答
0

进一步指出,这篇文章进一步澄清了事情。

于 2009-03-09T17:36:35.507 回答
-9

这是一个很好的问题,虽然很多人会回答这是堆栈分配和堆分配之间的区别,但基本的答案是底层系统向您暴露了一些不应该的东西。

当您分配内存时,您不必担心将其归还。系统应该足够聪明,可以确定您不再可以访问(指向或引用)它,因此它可以自动收回内存。

Java 和 C# 等较新的语言已经做到了这一点。

于 2009-03-09T17:33:53.407 回答