内存管理/手动内存管理
在每个应用程序中,您必须在使用之前分配新的存储空间,然后在完成使用后必须将所有这些存储空间返回给操作系统。虽然一些语言会为您处理所有这些,但像C或C++这样的低级语言有时需要程序员付出更多努力来管理内存。有几种方法可以做到这一点:隐式和显式。
隐式内存分配是内存,通常在系统堆栈上,由编译器分配。当您创建新变量时,就会发生这种情况
void my_func(void) {
int x;
char y;
int z[25];
...
}
在这里,当函数被调用时,在系统堆栈上为变量x
、y
和z
分配空间,并且当函数退出时,空间会自动回收。
有关在堆栈上分配的内存的更多信息,请参阅x86反汇编中的相关章节。 |
显式内存分配是通过指针,以及对内存管理函数的调用
#include <stdlib.h>
void my_func(void) {
int * z;
z = (int *)malloc(25);
...
}
在这里,我们仍然有变量z
,它仍然被用作 25 个整数的数组。但是,当函数退出时,此存储空间不会自动回收。这有一个额外的好处,即以这种方式创建的数组可以从函数中返回
正确 | 错误 |
---|---|
int *my_func(void) {
int * x;
x = (int *) malloc(25);
return x;
}
|
int *my_func(void) {
int x[25];
return x;
}
|
为什么一种方式正确,而另一种方式错误?答案是范围:在左侧的示例中,数组是在堆上创建的,并且内存不会在函数退出后被回收。这意味着内存仍然可以使用,即使在函数返回之后也是如此。然而,在右侧的示例中,内存是在堆栈上分配的,并且在函数退出时会消失。
还有很多地方需要在编译时未知的大小创建内存。考虑一个需要分配足够的存储空间来存储下载的网页 HTML 文本的网页浏览器。由于网页的大小可能各不相同,因此我们不可能在编译时知道它们的大小,我们必须在运行时使用malloc
或等效函数来分配存储空间。
在堆栈上分配的内存,其大小和范围是固定的,称为静态。使用malloc
在运行时分配的内存称为动态。
当您使用malloc
或类似方法从堆中分配内存时,您会从系统获得指向分配内存的指针。当您完成使用时,必须释放内存,将其返回给系统。未能返回您分配的所有内存称为内存泄漏。在 C 中,您可以使用free()
函数释放内存。
当您分配内存时,您必须始终注意将分配的内存释放回系统。问题在于:如果您的系统很复杂,需要分配许多小的内存块,您需要确保也释放所有这些单独的小块。如果您处于动态情况,内存不断被分配和释放,并且内存块必须保持可用以随机长度的时间,那么这可能更加困难。
为了克服这些复杂情况,可以使用自动内存管理器系统,如垃圾收集器,来尝试自动执行内存分配和释放。