内存类型

在运行一个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. HUAWEI网络设备恢复Console口密码

    密码遗忘 本章介绍了Console口丢失的处理方法,建议用户妥善保管密码,并定期修改. 恢复Console口密码 设备提供如下方法恢复Console口密码.•方法一:通过STelnet/Telnet登 ...

  2. 通过暗码去打开/关闭usb debug开关

    通过暗码去打开/关闭usb debug开关 通过暗码去打开/关闭usb debug开关1. Description2. Analysis3. Solution4. Summary 1. Descrip ...

  3. RocketMQ 原理:消息存储、高可用、消息重试、消息幂等性

    目录 消息存储 消息存储方式 非持久化 持久化 消息存储介质 消息存储与读写方式 消息存储结构 刷盘机制 同步刷盘 异步刷盘 小结 高可用 高可用实现 主从复制 负载均衡 消息重试 顺序消息重试 无序 ...

  4. 简述ASP.NET网站开发步骤

    新建解决方案 清除解决方案 重新生成解决方案 发布应用程序 设置配置文件 重命名配置文件 发布后生成的文件 IIS安装 安装完成后,输入http://localhost/出现 打开IIS 添加自己的网 ...

  5. golang中bufio和ioutil的使用

    bufio bufio包实现了带缓冲区的读写,是对文件读写的封装 bufio缓冲写数据 模式 含义 os.O_WRONLY 只写 os.O_CREATE 创建文件 os.O_RDONLY 只读 os. ...

  6. golang中通过bufio和os包读取终端中输入的一行带空格的数据

    1. 如果读取不带空格的数据可以使用fmt.Scan或fmt.Scanln读取一个或多个值,但是不能读取带空格的数据,可以使用bufio和os两个包结合 package main import ( & ...

  7. Python 安装MySQL 错误处理

    正常情况下如果使用python 连接数据库需要安装 python-MySQL 类库 #pip install python-MySQL 等待安装完成即可 使用时 import MySQLdb ==== ...

  8. Nginx全面介绍 什么是Nginx?

    目录 一:Nginx全面讲解 1.简介: 2.nginx的用武之地 3.关于代理(解析含义作用) 二:正向代理 三:反向代理 四:项目应用场景 五:正向代理与反向代理区别 1.正向代理 2.反向代理 ...

  9. linux正则sed实战案例详解

    目录 1. 将nginx.conf中的注释全部去掉 2. 将nginx.com中每一行之前增加注释 3.要求一键修改本机的ip 4.将/etc/passwd中的root修改成ROOT 1. 将ngin ...

  10. 无法自动装配。未找到 ‘xxxx’ 类型的 Bean。

    无法自动装配.未找到 'xxxx' 类型的 Bean. 1.解决办法 打开设置,找到编辑器->检查.把"自动装配Bean类",取消勾选,点击应用.确定.