操作系统导论 | 第 14 章笔记

99 阅读3分钟

第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 这样的语言中,释放分配的内存块。

  1. 忘记分配内存
  2. 没有分配足够的内存
  3. 忘记初始化分配的内存
  4. 忘记释放内存
  5. 在用完之前释放内存
  6. 重复释放内存
  7. 错误地调用free()

14.5 底层操作系统支持

malloc()和free()不是系统调用,而是库调用。

14.6 其他调用

calloc()分配内存,并在返回之前将其置零。

14.7 小结

一些处理内存分配的API。

命令行工具

  • gdb:一个调试器
  • valgrind:可帮助查找程序中内存泄漏和其他隐藏的内存问题

“开启掘金成长之旅!这是我参与「掘金日新计划 · 2 月更文挑战」的第 20 天,点击查看活动详情