第14章 插叙:内存操作API
介绍UNIX操作系统内存分配接口,主要关注如何分配和管理内存。
14.1 内存类型
运行一个C程序的时候,会分配两种类型的内存。
第一种称为栈内存(短期内存),它的申请和释放操作是编译器来隐式管理的,所以有时也称为自动内存。
特性:短期,隐式
如C中的int x即是为整形变量x申请栈内存。
void func() {
int x; // declares an integer on the stack
...
}
进入func()函数时,在栈上开辟空间;从该函数退出时,编译器释放内存。
第二种称为堆内存(长期内存),其中所有的申请和释放操作都由程序员显式地完成。
特性:长期,显式
void func() {
int *x = (int *) malloc(sizeof(int));
...
}
- 首先编译器看到指针的声明(int * x)时,知道为一个整型指针分配空间
- 随后,当程序调用malloc()时,它会在堆上请求整数的空间,函数返回这样一个整数的地址(成功时,失败时则返回 NULL),然后将其存储在栈中以供程序使用。
14.2 malloc()调用
malloc函数:传入要申请的堆空间的大小,成功返回一个指向新申请空间的指针,失败返回NULL。
malloc 只需要一个 size_t 类型参数,该参数表示你需要多少个字节。然而,大多数程序员并不会直接传入数字(比如 10)。实际上,这样做会被认为是不太好的形式。替代方案是使用各种函数和宏。例如,为了给双精度浮点数分配空间,只要这样:
double *d = (double *) malloc(sizeof(double));
另一个需要注意的地方是使用字符串。如果为一个字符串声明空间,请使用以下习惯用法:malloc(strlen(s) + 1),它使用函数strlen()获取字符串的长度,并加上1,以便为字符串结束符留出空间。这里使用sizeof()可能会导致麻烦。
malloc()返回一个指向void类型的指针,这样做只是C中传回地址的方式,让程序员决定如何处理它。程序员可以进一步使用所谓强制类型转换(cast),如上面的示例中程序员将返回类型的malloc()强制转换为指向double的指针。
14.3 free()调用
调用free():释放不再使用的堆内存。
free()期望你只传入之前从malloc()得到的一个指针。
14.4 常见错误
许多新语言都支持自动内存管理(automatic memory management),当你调用类似malloc()的机制来分配内存时(通常用 new 或类似的东西来分配一个新对象),你永远不需要调用某些东西来释放空间。实际上, 垃圾收集器(garbage collector)会运行,找出你不再引用的内存,替你释放它。
但较现代的语言中内存泄漏仍然是一个问题(常见错误4)。长远来看,作为程序员的目标之一是养成良好的习惯。其中一个习惯是理解如何管理内存,并在 C 这样的语言中,释放分配的内存块。
- 忘记分配内存
- 没有分配足够的内存
- 忘记初始化分配的内存
- 忘记释放内存
- 在用完之前释放内存
- 重复释放内存
- 错误地调用free()
14.5 底层操作系统支持
malloc()和free()不是系统调用,而是库调用。
14.6 其他调用
calloc()分配内存,并在返回之前将其置零。
14.7 小结
一些处理内存分配的API。
命令行工具
- gdb:一个调试器
- valgrind:可帮助查找程序中内存泄漏和其他隐藏的内存问题
“开启掘金成长之旅!这是我参与「掘金日新计划 · 2 月更文挑战」的第 20 天,点击查看活动详情”