肯尼斯·里科《C和指针》第11章 动态内存分配(1)动态内存分配的基础知识

news2024/11/29 0:38:13

数组的元素存储于内存中连续的位置上。当一个数组被声明时,它所需要的内存在编译时就被分配。但是,也可以使用动态内存分配在运行时为它分配内存。在本章中,我们将研究这两种技巧的区别,看看什么时候应该使用动态内存分配以及怎样进行动态内存分配。

11.1 为什么使用动态内存分配

在声明数组时,必须用一个编译时常量指定数组的长度。但是,数组的长度常常在运行时才知道,这是因为它所需要的内存空间取决于输入数据。例如,一个用于计算学生等级和平均分的程序可能需要存储一个班级所有学生的数据,但不同班级的学生数量可能不同。在这些情况下,我们通常采取的方法是声明一个较大的数组,它可以容纳可能出现的最多元素。

这种方法的优点是简单,但它有好几个缺点。首先,这种声明在程序中引入了人为的限制,如果程序需要使用的元素数量超过了声明的长度,它就无法处理这种情况。要避免这种情况,显而易见的方法是把数组声明得更大一些,但这种做法使它的第2个缺点进一步恶化。如果程序实际需要的元素数量比较少时,巨型数组的绝大部分内存空间都被浪费了。这种方法的第3个缺点是如果输入的数据超过了数组的容纳范围,程序必须以一种合理的方式做出响应。它不应该因为一个异常而失败,但也不应该打印出看上去正确实际上却是错误的结果。实现这一点所需要的逻辑其实很简单,但人们在头脑中很容易形成“数组永远不会溢出”这个概念,这就诱使他们不去实现这种方法。

批在:这里感觉不如上课讲的清楚,我记得上课讲的是一种动态内存分配的实现方式,如果输入的数据超出了初始的空间,就把需要使用的空间扩大两倍。依此类推。

11.2 malloc和free

C函数库提供了malloc和free两个函数,分别用于执行动态内存分配和释放。这些函数维护一个可用内存池。当一个程序另外需要一些内存时,它就调用malloc函数,malloc从内存池中提取一块合适的内存,并向该程序返回一个指向这块内存的指针。这块内存此时并没有以任何方式进行初始化。如果对这块内存进行初始化非常重要,你要么自己动手对它进行初始化,要么使用calloc函数(在下一节描述)。当一块以前分配的内存不再使用时,程序调用free函数把它归还给内存池供以后之需。

这两个函数的原型如下所示,它们都在头文件stdlib.h中声明:

void *malloc(size_t size);
void free(void *pointer);

malloc的参数就是需要分配的内存字节(字符)数。如果内存池中的可用内存可以满足这个需求,malloc就返回一个指向被分配的内存块起始位置的指针。

malloc所分配的是一块连续的内存。例如,如果请求它分配100字节的内存,那么它实际分配的内存就是100个连续的字节,并不会分开位于两块或多块不同的内存。同时,malloc实际分配的内存有可能比请求的稍微多一点。但是,这个行为是由编译器定义的,所以不能指望它肯定会分配比请求更多的内存。

如果内存池是空的,或者它的可用内存无法满足请求,会发生什么情况呢?在这种情况下,malloc函数向操作系统请求,要求得到更多的内存,并在这块新内存上执行分配任务。如果操作系统无法向malloc提供更多的内存,malloc就返回一个NULL指针。因此,对每个从malloc返回的指针都进行检查,确保它并非NULL,这非常重要。

free的参数必须要么是NULL,要么是一个先前从malloc、calloc或realloc(稍后描述)返回的值。向free传递一个NULL参数不会产生任何效果。

malloc又是如何知道所请求的内存需要存储的是整数、浮点值、结构还是数组呢?它并不知情——malloc返回一个类型为void *的指针,正是缘于这个原因。标准表示一个void *类型的指针可以转换为其他任何类型的指针。但是,有些编译器,尤其是那些老式的编译器,可能要求你在转换时使用强制类型转换。

对于要求边界对齐的机器,malloc所返回的内存的起始位置将始终能够满足对边界对齐要求最严格的类型的要求。

批注:记的话其实就是memory allocation吧?

11.3 calloc和realloc

另外还有两个内存分配函数:calloc和realloc。它们的原型如下所示:

void *calloc(size_t num_elements, size_t element_size);
void realloc(void *ptr, size_t new_size);

calloc也用于分配内存。malloc和calloc之间的主要区别是后者在返回指向内存的指针之前把它初始化为0。这个初始化常常能带来方便,但如果你的程序只是想把一些值存储到数组中,那么这个初始化过程纯属浪费时间。calloc和malloc之间另一个较小的区别是它们请求内存数量的方式不同。calloc的参数包括所需元素的数量和每个元素的字节数。根据这些值,它能够计算出总共需要分配的内存。

realloc函数用于修改一个原先已经分配的内存块的大小。使用这个函数,可以使一块内存扩大或缩小。如果它用于扩大一个内存块,那么这块内存原先的内容依然保留,新增加的内存添加到原先内存块的后面,新内存并未以任何方法进行初始化。如果它用于缩小一个内存块,该内存块尾部的部分内存便被拿掉,剩余部分内存的原先内容依然保留。

如果原先的内存块无法改变大小,realloc将分配另一块正确大小的内存,并把原先那块内存的内容复制到新的块上。因此,在使用realloc之后,就不能再使用指向旧内存的指针,而是应该改用realloc所返回的新指针。

最后,如果realloc函数的第一个参数是NULL,那么它的行为就和malloc一模一样。

批注:初始化为0,所以是clear allocation?

11.4 使用动态分配的内存

这里有一个例子,它用malloc分配一块内存:

符号NULL定义于stdio.h,它实际上是字面值常量0。它在这里起着视觉提醒器的作用,提醒我们进行测试的值是一个指针而不是整数。

如果内存分配成功,那么我们就拥有了一个指向100字节的指针。在整型为4字节的机器上,这块内存将被当作25个整型元素的数组,因为pi是一个指向整型的指针。

如果你的目标就是获得足够存储25个整数的内存,这里有一个更好的技巧来实现这个目的。

pi = malloc( 25 * sizeof(int))

这个方法更好一些,因为它是可移植的。即使是在整数长度不同的机器上,它也能获得正确的结果。

既然已经有了一个指针,那么该如何使用这块内存呢?当然可以使用间接访问和指针运算来访问数组的不同整数位置,下面这个循环就是这样做的。它把这个新分配的数组的每个元素都初始化为0:

可以看到,不仅可以使用指针,也可以使用下标。下面的循环所执行的任务和前面一个相同:

感觉和之前上课的区别在于这本书会写的更详细而且更好理解一点。。。 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1425268.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【数学】【记忆化搜索 】【动态规划】964. 表示数字的最少运算符

作者推荐 【动态规划】【字符串】【表达式】2019. 解出数学表达式的学生分数 本文涉及知识点 动态规划汇总 数学 记忆化搜索 LeetCoce964表示数字的最少运算符 给定一个正整数 x,我们将会写出一个形如 x (op1) x (op2) x (op3) x … 的表达式,其中每…

【C++】类和对象万字详解

目录 一、类与对象 1、类是什么 二、类和对象的基础知识 2.1 定义类:成员变量和成员函数 2.2 创建对象:实例化一个类的对象。 2.3对象的生命周期:构造函数和析构函数。 a. 构造函数 b. 析构函数 c.小结: 三、成员变量和…

vue3使用is动态切换组件报错Vue received a Component which was made a reactive object.

vue3使用is动态切换组件,activeComponent用ref定义报错 Vue received a Component which was made a reactive object. This can lead to unnecessary performance overhead, and should be avoided by marking the component with markRaw or using shallowRef ins…

Ansible基础及常用模块

目录 1.前言 Ansible Ansible的特性 2.ansible环境安装部署 管理端安装ansible(192.168.88.22) ansible目录结构 配置主机清单 配置密钥对验证 3.ansible命令行模块 command 模块 shell 模块 ​编辑cron 模块 user 模块 group 模块 copy 模块 file 模块 hostn…

表达式(C语言)

目录 表达式求值 整型提升 ​编辑整型提升的意义 如何进行整体提升? 算术转换 问题表达式解析 表达式1 表达式2 表达式3 表达式4 表达式5 总结 表达式求值 整型提升 C语言中整型算术运算总是至少以缺省整型类型的精度来进行的 为了获得这个精度&#x…

C++: 类的简单介绍(三)———构造函数的初步认识

概念: 构造函数是一个特殊的成员函数,名字与类名相同,创建类类型对象时由编译器自动调用,以保证 每个数据成员都有 一个合适的初始值,并且在对象整个生命周期内只调用一次 需要注意的是,构造函数虽然名称叫构造&…

多线程编程4——线程安全问题

一、线程之间是并发执行的,是抢占式随机调度的。 多个线程之间是并发执行的,是随机调度的。我们只能确保同一个线程中代码是按顺序从上到下执行的,无法知道不同线程中的代码谁先执行谁后执行。 比如下面这两个代码: 代码一&…

接口性能优化常见12式

目录 1.批处理 2.异步处理 3.空间换时间 4.预处理 5.池化思想 6.串行改并行 7.索引 8.避免大事务 9.优化程序结构 10.深分页问题 11.SQL优化 12.锁粒度避免过粗 1.批处理 批量思想:批量操作数据库,这个很好理解,我们在循环插入场…

GSM-TRIAL-21.04.9-VMware-Workstation.OVA安装教程,GreenBone虚拟机安装教程

将GSM-TRIAL-21.04.9-VMware-Workstation.ova用VMware打开 先设置好网络和内存: 1、打开虚拟机,显示:你的GSM还不能完全正常工作。您想现在完成设置吗? 点击yes 2、创建用户,一会儿登录网页要用,点击yes 3、创建用户…

提升网站性能的秘诀:为什么Nginx是高效服务器的代名词?

在这个信息爆炸的时代,每当你在浏览器中输入一个网址,背后都有一个强大的服务器在默默地工作。而在这些服务器中,有一个名字你可能听说过无数次——Nginx。今天,就让我们一起探索这个神奇的工具。 一、Nginx是什么 Nginx&#x…

Zoho Mail 2023:回顾过去,展望未来:不断进化的企业级邮箱解决方案

当我们告别又一个非凡的一年时,我们想回顾一下Zoho Mail如何融合传统与创新。我们迎来了成立15周年,这是一个由客户、合作伙伴和我们的敬业团队共同庆祝的里程碑。与我们一起回顾这段旅程,探索定义Zoho Mail历史篇章的敏捷性、精确性和创新性…

分布式搜索引擎_学习笔记_3

分布式搜索引擎03 0.学习目标 1.数据聚合 **聚合(aggregations)**可以让我们极其方便的实现对数据的统计、分析、运算。例如: 什么品牌的手机最受欢迎?这些手机的平均价格、最高价格、最低价格?这些手机每月的销售…

各品牌主板快速启动热键对照表及CMOS进入方法

各品牌主板快速启动热键对照表 主板品牌 启动按键 笔记本品牌 启动按键 主机品牌 启动按键 华硕主板 F8 联想笔记本 F12 联想台式机 F12 技嘉主板 F12 宏碁笔记本 F12 惠普台式机 F12 微星主板 F11 华硕笔记本 ESC 宏碁台式机 F12 梅捷主板 F9 惠普笔…

光伏设计系统都具备哪些功能?

随着可再生能源的日益重要,光伏能源已成为我们能源结构中的重要组成部分。而光伏设计系统作为实现光伏能源高效利用的关键,其功能也日益丰富和多样化。本文将探讨光伏设计系统所具备的主要功能。 1.数据分析与模拟 光伏设计系统能够对大量的数据进行分…

iOS 文件分割保存加密

demo只是验证想法,没有做很多异常处理 默认文件是大于1KB的,对于小于1KB的没有做异常处理demo中文件只能分割成2个,可以做成可配置的N个文件分割拼接还可以使用固定的二进制数据,拼接文件开头或结尾 不论哪种拼法,目的…

InfluxDB数据的导入导出

Background influxdb支持将时序数据导出到文件,然后再将文件导入到数据库中,以此实现数据的迁移。 1、数据导出 语法: 示例: influx_inspect export -datadir "/var/lib/influxdb/data" -waldir "/var/lib/influ…

Django中的模板

目录 一:基本概念 二:模板继承 在Django中,模板是用于呈现动态内容的HTML文件。它们允许你将动态数据与静态模板结合起来,生成最终的HTML页面。 Django模板使用特定的语法和标签来插入动态内容。你可以在模板中使用变量、过滤器和标签来控…

Java技术栈 —— Hadoop入门(二)实战

Java技术栈 —— Hadoop入门(二) 一、用MapReduce对统计单词个数1.1 项目流程1.2 可能遇到的问题1.3 代码勘误1.4 总结 一、用MapReduce对统计单词个数 1.1 项目流程 (1) 上传jar包。 (2) 上传words.txt文件。 (3) 用hadoop执行jar包的代码,…

【Leetcode 514】自由之路 —— 动态规划

514. 自由之路 电子游戏“辐射4”中,任务 “通向自由” 要求玩家到达名为 “Freedom Trail Ring” 的金属表盘,并使用表盘拼写特定关键词才能开门。 给定一个字符串ring,表示刻在外环上的编码;给定另一个字符串key,表…

Linux 驱动开发基础知识——总线设备驱动模型(八)

个人名片: 🦁作者简介:学生 🐯个人主页:妄北y 🐧个人QQ:2061314755 🐻个人邮箱:2061314755qq.com 🦉个人WeChat:Vir2021GKBS 🐼本文由…