简单的网站开发工具,wordpress网站转app插件下载,手机软件开发培训学校,wordpress 画廊 插件《操作系统导论》第14章读书笔记#xff1a;插叙#xff1a;内存操作API —— 杭州 2024-03-30 夜 文章目录 《操作系统导论》第14章读书笔记#xff1a;插叙#xff1a;内存操作API1.内存类型1.1.栈内存#xff1a;它的申请和释放操作是编译器来隐式管理的#xff0c;所…《操作系统导论》第14章读书笔记插叙内存操作API —— 杭州 2024-03-30 夜 文章目录 《操作系统导论》第14章读书笔记插叙内存操作API1.内存类型1.1.栈内存它的申请和释放操作是编译器来隐式管理的所以有时也称为自动automatic内存。1.2.堆heap内存:其中所有的申请和释放操作都由程序员显式地完成。 2.malloc()调用3.free()调用4.常见错误4.1.忘记分配内存4.2.没有分配足够的内存缓冲区溢出buffer overflow4.3.忘记初始化分配的内存4.4.忘记释放内存内存泄露memory leak4.5.在用完之前释放内存悬挂指针dangling pointer4.6.反复释放内存4.7.错误地调用free()4.8.补充为什么在你的进程退出时没有内存泄露 5.底层操作系统支持6.其他调用和小结7.补充笔记malloc()、calloc()、realloc()比较 1.内存类型
1.1.栈内存它的申请和释放操作是编译器来隐式管理的所以有时也称为自动automatic内存。
void func() {int x; // declares an integer on the stack...
}1.2.堆heap内存:其中所有的申请和释放操作都由程序员显式地完成。
void func() {int *x (int *) malloc(sizeof(int));...
}关于这一小段代码有两点说明。首先你可能会注意到栈和堆的分配都发生在这一行首先编译器看到指针的声明int * x时知道为一个整型指针分配空间随后当程序调用malloc()时它会在堆上请求整数的空间函数返回这样一个整数的地址成功时失败时则返回NULL然后将其存储在栈中以供程序使用。 2.malloc()调用
malloc 函数非常简单传入要申请的堆空间的大小它成功就返回一个指向新申请空间的指针失败就返回NULL。man 手册展示了使用malloc 需要怎么做在命令行输入man malloc你会看到
#include stdlib.h
...
void *malloc(size_t size);你也可以传入一个变量的名字而不只是类型给sizeof()但在一些情况下可能得不到你要的结果所以要小心使用。例如看看下面的代码片段
int *x malloc(10 * sizeof(int));
printf(%d\n, sizeof(x));在第一行我们为10个整数的数组声明了空间这很好很漂亮。但是当我们在下一行使用sizeof()时它将返回一个较小的值例如4在32位计算机上或8在64 位计算机上。原因是在这种情况下sizeof()但为我们只是问一个整数的指针有多大而不是我们动态分配了多少内存。 但是有时sizeof()的确如你所期望的那样工作
int x[10];
printf(%d\n, sizeof(x));在这种情况下编译器有足够的静态信息知道已经分配了40个字我。另一个需要注意的地方是使用字符串。如果为一个字符串声明空间请使用以下习惯用法malloc(strlen(s) 1)它使用函数strlen()获取字符串的长度并加上1以便为字符串结束符留出空间。这里使用sizeof()可能会导致麻烦。你也许还注意到malloc()返回一个指向void类型的指针。这样做只是C中传回地址的方式让程序员决定如何处理它。程序员将进一步使用所谓的强制类型转换cast在我们上面的示例中程序员将返回类型的malloc()强制转换为指向double的指针。强制类型转换实实上没干什么事只是告诉编译器和其他可能正在读你的代码的程序员“是的我知道我在做什么。”通过强制转换malloc()的结果程序员只是在给人一些信心强制转换不是程序正确所必须的。 3.free()调用
事实证明分配内存是等式的简单部分。知道何时、如何以及是否释放内存是困难的部分。要释放不再使用的堆内存程序员只需调用free()
int *x malloc(10 * sizeof(int));
...
free(x);该函数接受一个参数即一个由malloc()返回的指针。因此你可能会注意到分配区域的大小不会被用户传入必须由内存分配库本身记录追踪。 4.常见错误
4.1.忘记分配内存
许多例程在调用之前都希望你为它们分配内存。例如例程strcpy(dst, src)将源字符串中的字符串复制到目标指针。但是如果不小心你可能会这样做
char *src hello;
char *dst; // oops! unallocated
strcpy(dst, src); // segfault and die运行这段代码时可能会导致段错误segmentation fault。
仅仅因为程序编译过了甚至正确运行了一次或多次并不意味着程序是正确的。
在这个例子中正确的代码可能像这样
char *src hello;
char *dst (char *) malloc(strlen(src) 1);
strcpy(dst, src); // work properly或者你可以用strdup()让生活本加轻松。
4.2.没有分配足够的内存缓冲区溢出buffer overflow
char *src hello;
char *dst (char *) malloc(strlen(src)); // too small!
strcpy(dst, src); // work properly奇怪的是这个程序通常看起来会正确运行这取决于如何实现malloc 和许多其他细节。在某些情况下当字符串拷贝执行时它会在超过分配空间的末尾处写入一个字节但在某些情况下这是无害的可能会覆盖不再使用的变量。在某些情况下这些溢出可能具有令人难以置信的危害实实上是系统中许多安全漏洞的来源。在其他情况下malloc库总是分配一些额外的空间因此你的程序实际上不会在其他某个变量的值上涂写并且工作得很好。还有一些情况下该程序确实会发生故障和崩溃。
一个宝贵的教训即使它正确运行过一次也不意味着它是正确的。
4.3.忘记初始化分配的内存
4.4.忘记释放内存内存泄露memory leak
另一个常见错误称为内存泄露memory leak如果忘记释放内存就会发生。
在长时间运行的应用程序或系统如操作系统本身中这是一个巨大的问题因为缓慢泄露的内存会导致内存不足此时需要重新启动。因此一般来说当你用完一段内存时应该确保释放它。请注意使用垃圾收集语言在这里没有什么帮助如果你仍然拥有对某块内存的引用那么垃圾收集器就不会释放它因此即使在较现代的语言中内存泄露仍然是一个问题。在某些情况下不调用free()似乎是合理的。例如你的程序运行时间很短很块就会退出。在这种情况下当进程死亡时操作系统将清理其分配的所有页面因此不会发生内存泄露。虽然这肯定“有效”请参阅后面的补充但这可能是一个坏习惯所以请谨慎选择这样的策略。长远来看作为程序员的目标之一是养成良好的习惯。其中一个习惯是理解如何管理内存并在C这样的语言中释放分配的内存块。即使你不这样做也可以逃脱惩罚建议还是养成习惯释放显式分配的每个字节。
4.5.在用完之前释放内存悬挂指针dangling pointer
有时候程序会在用完之前释放内存这种错误称为悬挂指针dangling pointer正如你猜测的那样这也是一件坏事。随后的使用可能会导致程序崩溃或覆盖有效的内存例如你调用了free()但随后再次调用malloc()来分配其他内容这重新利用了错误释放的内存。
4.6.反复释放内存
4.7.错误地调用free()
4.8.补充为什么在你的进程退出时没有内存泄露 当你编写一个短时间运行的程序时可能会使用malloc()分配一些空间。程序运行并即将完成是否需要在退出前调用几次free()虽然不释放似乎不对但在真正的意义上没有任何内存会“丢失”。原因很简单系统中实际存在两级内存管理。 第一级是由操作系统执行的内存管理操作系统在进程运行时将内存交给进程并在进程退出或以其他方式结束时将其回收。第二级管理在每个进程中例如在调用malloc()和free()时在堆内管理。即使你没有调用free()并因此泄露了堆中的内存操作系统也会在程序结束运行时收回进程的所有内存包括用于代码、栈以及相关堆的内存页。无论地址空间中堆的状态如何操作系统都会在进程终止时收回所有这些页面从而确保即使没有释放内存也不会丢失内存。 因此对于短时间运行的程序泄露内存通常不会导致任何操作问题尽管它可能被认为是不好的形式。如果你编写一个长期运行的服务器例如Web 服务器或数据库管理系统它永远不会退出泄露内存就是很大的问题最终会导致应用程序在内存不足时崩溃。当然在某个程序内部泄露内存是一个更大的问题操作系统本身。这再次向我们展示编写内核代码的人工作是辛苦的…… 5.底层操作系统支持 你可能已经注意到在讨论malloc()和free()时我们没有讨论系统调用。原因很简单它们不是系统调用而是库调用。因此malloc库管理虚拟地址空间内的空间但是它本身是建立在一些系统调用之上的这些系统调用会进入操作系统来请求本多内存或者将一些内容释放回系统。 最后你还可以通过mmap()调用从操作系统获取内存。通过传入正确的参数mmap()可以在程序中创建一个匿名anonymous内存区域——这个区域不与任何特定文件相关联而是与交换空间swapspace相关联稍后我们将在虚拟内存中详细讨论。这种内存也可以像堆一样对待并管理。阅读mmap()的手册页以获取本多详细信息。 6.其他调用和小结 7.补充笔记malloc()、calloc()、realloc()比较
当涉及到动态内存分配时malloc(), calloc(), 和 realloc() 是 C 语言标准库中的三个重要函数。以下是这三个函数的比较表格
特征/函数malloc()calloc()realloc()功能分配指定大小的内存块分配指定数量的连续内存块并将每一块初始化为 0改变先前分配的内存块的大小参数需要分配的内存块大小以字节为单位内存块的数量和每个块的大小以字节为单位指向先前分配的内存块的指针以及新的内存大小返回值指向分配的内存块的指针如果分配失败则返回 NULL指向分配且初始化的内存块的指针如果分配失败则返回 NULL指向重新分配的内存块的指针如果分配失败则返回 NULL初始化不初始化内存块内存块的内容不确定将内存块的每个字节都初始化为 0不初始化新分配的内存部分旧内存内容保持不变至新大小性能通常比 calloc() 快因为不初始化内存可能比 malloc() 慢因为初始化内存性能依赖于给定的内存块和分配大小适用性当不需要初始化内存时使用当需要初始化数组或多个相同类型的对象时使用当需要调整先前分配的内存大小时使用示例代码int *ptr (int*)malloc(sizeof(int) * n);int *ptr (int*)calloc(n, sizeof(int));ptr (int*)realloc(ptr, sizeof(int) * n);
注意
malloc() 和 calloc() 在失败时都会返回 NULL因此在使用这些函数后应该检查返回值以确认内存分配是否成功。realloc() 在扩大内存块时可能会移动内存块到新的位置如果是这样它会复制旧内存内容到新位置并释放旧内存。如果 realloc() 的第一个参数是 NULL它等价于 malloc()。在使用完分配的内存后你应该使用 free() 函数释放它以避免内存泄漏。