内存类型

在运行一个C程序的时候,会分配两种类型的内存。第一种称为栈内存,它的申请和释放操作是编译器来隐式管理的,所以有时也称为自动内存。假设需要在func()函数中为一个整形变量x申请空间,我们只需要这样:

void func() {
int x; // declares an integer on the stack...
}

编译器完成剩下的事情,确保在你进入 func() 函数的时候,在栈上开辟空间。当你从该函数退出时,编译器释放内存。因此,如果希望将某些信息存在于函数调用之外,建议不要将它们放在栈上。

第二种类型的内存是堆内存,其中所有的申请和释放操作都由程序员显式地完成。下面的例子展示了如何在堆上分配一个整数,得到指向它的指针:

void func() {
int *x = (int *)malloc(sizeof(int));
...
}

注意到栈和堆的分配都发生在这一行:编译器看到指针的声明(int *x)时,知道为一个整型指针分配空间,随后,当程序调用malloc()时,它会在堆上请求整数的空间,函数返回这样一个整数的地址(失败时则返回NULL),然后将其存储在栈中以供程序使用。要释放不再使用的堆内存,我们只需要调用free()

int *x = malloc(10 * sizeof(int));
free(x);

free()接受一个由malloc()返回的指针作为参数,分配区域的大小不会被传入,必须由内存分配库本身记录追踪。

常见错误

忘记分配内存

下面这段代码会可能会导致段错误,因为我们没有给dst分配相应的内存就使用它。

char *src = "hello";
char *dst; // oops! unallocated
strcpy(dst, src); // segfault and die

没有分配足够的内存

另一个相关的错误是没有分配足够的内存,有时称为缓冲区溢出。

char *src = "hello";
char *dst = (char *)malloc(strlen(src)); // too small!
strcpy(dst, src); // work properly

这段程序能否正确运行,取决于如何实现malloc()和许多其他细节。当字符串拷贝执行时,它会在超过分配空间的末尾处写入一个字节,在某些情况下这是无害的,它可能会覆盖不再使用的变量。但是在另一些情况下,这些溢出可能具有令人难以置信的危害。有些malloc()实现总是分配一些额外的空间,因此程序实际上不会在其他某个变量的值上涂写,并且工作得很好。

忘记初始化分配的内存

如果我们正确地调用了malloc(),但是忘记在新分配的空间中填写一些值,那么程序可能会从堆中读取某些未知或有害的数据。

忘记释放内存

如果程序员忘记释放掉申请的内存空间,就会发生内存泄露(memory leak)。在长时间运行的应用程序或系中,这是一个巨大的问题,因为缓慢泄露的内存会导致内存不足,此时需要重新启动。因此,当用完一段内存时,应该确保释放它。请注意,使用垃圾收集语言在这里没有什么帮助:如果你仍然拥有对某块内存的引用,那么垃圾收集器就不会释放它,因此即使在较现代的语言中,内存泄露仍然是一个问题。

在用完之前释放内存

有时候程序会在用完之前释放内存,这种错误称为悬挂指针(dangling pointer)。随后的使用可能会导致程序崩溃或覆盖有效的内存(例如,调用了free(),但随后再次调用malloc()来分配其他内容,这重新利用了错误释放的内存)。

反复释放内存

程序有时还会不止一次地释放内存,这被称为重复释放,这样做的结果是未定义的。

错误地调用free()

free()期望我们只传入之前从malloc()得到的一个指针,如果传入其他值,坏事就有可能发生,我们应该避免这种无效的释放。

底层操作系统支持

malloc()free()不是系统调用,而是库调用。malloc()库管理虚拟地址空间内的空间,但是它本身是建立在一些系统调用之上的,这些系统调用会进入操作系统,来请求更多内存或者将一些内容释放回系统。一个这样的系统调用叫作brk,它被用来改变程序分断(break)的位置:堆结束的位置。它需要一个参数(新分断的地址),从而根据新分断是大于还是小于当前分断,来增加或减小堆的大小。另一个调用sbrk要求传入一个增量,但目的是类似的。brk与sbrk被内存分配库使用,并不能直接调用它们。此外,还可以通过mmap()调用从操作系统获取内存。通过传入正确的参数,mmap()可以在程序中创建一个匿名内存区域——这个区域不与任何特定文件相关联,而是与交换空间相关联,并且它也可以像堆一样对待并管理。

其他调用

内存分配库还支持一些其他调用。例如,calloc()分配内存,并在返回之前将其置零。realloc()创建一个新的更大的内存区域,将旧区域复制到其中,并返回新区域的指针。

《操作系统导论》第14章 | 内存操作API的更多相关文章

  1. Delphi内存操作API函数(备查,并一一学习)

    Delphi内存操作API函数System.IsMemoryManagerSet;System.Move;System.New;System.ReallocMem;System.ReallocMemo ...

  2. 【阅读笔记】《C程序员 从校园到职场》第五章 内存操作

    参考:   让你提前认识软件开发(8):memset()与memcpy()函数  https://blog.csdn.net/zhouzxi/article/details/22478081 让你提前 ...

  3. C#高级编程9 第14章 内存管理和指针

    C#高级编程9 内存管理和指针 后台内存管理 1) 值数据类型 在处理器的虚拟内存中有一个区域,称为栈,栈存储变量的浅副本数据,通过进入变量的作用域划分区域,通过离开变量的作用域释放. 栈的指针指向栈 ...

  4. 【二代示波器教程】第14章 uCOS-III操作系统版本二代示波器实现

    第14章      uCOS-III操作系统版本二代示波器实现 本章教程为大家讲解uCOS-III操作系统版本的二代示波器实现.主要讲解RTOS设计框架,即各个任务实现的功能,任务间的通信方案选择,任 ...

  5. 【翻译】《深入解析windows操作系统第6版下册》第10章:内存管理

    [翻译]<深入解析windows操作系统第6版下册>第10章:内存管理(第一部分) [翻译]<深入解析windows操作系统第6版下册>第10章:内存管理(第二部分) [翻译] ...

  6. Java API —— IO流(数据操作流 & 内存操作流 & 打印流 & 标准输入输出流 & 随机访问流 & 合并流 & 序列化流 & Properties & NIO)

    1.操作基本数据类型的流     1) 操作基本数据类型 · DataInputStream:数据输入流允许应用程序以与机器无关方式从底层输入流中读取基本 Java 数据类型.应用程序可以使用数据输出 ...

  7. -1-4 java io java流 常用流 分类 File类 文件 字节流 字符流 缓冲流 内存操作流 合并序列流

      File类 •文件和目录路径名的抽象表示形式 构造方法 •public File(String pathname) •public File(String parent,Stringchild) ...

  8. ASM:《X86汇编语言-从实模式到保护模式》第14章:保护模式下的特权保护和任务概述

    ★PART1:32位保护模式下任务的隔离和特权级保护  这一章是全书的重点之一,这一张必须要理解特权级(包括CPL,RPL和DPL的含义)是什么,调用门的使用,还有LDT和TSS的工作原理(15章着重 ...

  9. Linux就这个范儿 第14章 身在江湖

    Linux就这个范儿 第14章 身在江湖 “有人的地方就有江湖”,如今的计算机世界就像一个“江湖”.且不说冠希哥有多么无奈,把微博当QQ的局长有多么失败,就说如此平凡的你我什么时候就成了任人摆布的羔羊 ...

随机推荐

  1. Python面向对象时最常见的3类方法

    为了节省读友的时间,先上结论(对于过程和细节感兴趣的读友可以继续往下阅读,一探究竟): [结论] 类中定义的方法类型 关键词 本质含义 如何定义 如何调用 使用场景举例 实例方法 一般无任何修饰时,默 ...

  2. 离线下载第三方Python包

    1.进入Python第三方包下载地(https://pypi.org/)搜索自己需要的包 2.下载需要的包的版本 3.将.whl格式的文件更改为.zip文件,并且解压 4.将解压的2个文件放到Pyth ...

  3. php中使用CURL之php curl详解

    curl是个什么东西?简单地说就是,curl是一个库,能让你通过URL和许多不同种的服务器进行勾搭.搭讪和深入交流,并且还支持许多协议.并且人家还说了curl可以支持https认证.http post ...

  4. python极简教程06:生成式和装饰器

    测试奇谭,BUG不见. 这一场,主讲python的生成式和装饰器. 目的:掌握四种生成式(列表.生成器.集合.字典),装饰器的原理和使用. 生成式 01 什么是生成式? 能够用一行代码,快速高效的生成 ...

  5. ClassCastException: java.util.Date cannot be cast to java.sql.Date

    解决办法 /** * 单个方法,作用,根据输入的day:yyyy-mm-dd格式的字符日期,将数据库中的该天所有数据更新为0 * 0表示假期 * @param day * @throws SQLExc ...

  6. netty基础知识

    参考 http://www.infoq.com/cn/articles/netty-high-performance 1. 传统 RPC 调用性能差的三宗罪 1)网络传输方式问题 2)序列化方式问题 ...

  7. 【Azure 应用服务】Azure Mobile App (NodeJS) 的服务端部署在App Service for Windows中出现404 Not Found -- The resource you are looking for has been removed, had its name changed, or is temporarily unavailable.

    问题描述 使用NodeJS的后端应用,开发一个Mobile App的服务端,手机端通过REST API来访问获取后端数据.在本地编译好后,通过npm start启动项目,访问效果如下: 但是,当把项目 ...

  8. WebSphere--WAS概念和原理解析

    WebSphere--WAS概念和原理解析--tigergao收录于2021/04/25

  9. JVM内存模型(五)

    一.JVM内存模型 1.1.与运行时数据区     前面讲过了运行时数据区那接下来我们聊下内存模型,JVM的内存模型指的是方法区和堆:在很多情况下网上讲解会把内存模型和运行时数据区认为是一个东西,这是 ...

  10. markdown介绍和使用(超全建议收藏)

    Markdown介绍 Markdown 其实在 2004 年就有了,不过之前一直很小众,这几年随着相关应用平台的发展,Markdown以其独到的优势迅速火起来了.Markdown编辑器使用一套格式标记 ...