c语言动态内存分布
前言:
随着我们深入的学习c语言,之前使用的静态内存分配已经难以满足我们的实际需求。比如前面我们的通讯录功能的实现,如果只是静态内存分配,那么也就意味着程序开始的内存分配大小就是固定的,应该开多大的空间呢?开大了是浪费,开小了又不能满足自己的需求。而动态内存分配可以完美的解决这个问题,真正地做到需要多少空间就开多大的空间(根据需要动态地分配和释放内存空间).
总的来说,动态内存分配比静态内存分配更灵活,效率也更高,避免了空间的浪费。
下面就开始动态内存的学习吧。
1.动态内存函数:
1.malloc
2.free
3.calloc
4.realloc
这些函数都声明在stdlib头文件中。
1.1 malloc和free
malloc:
void* malloc (size_t size);
malloc函数向内存申请一块连续可用的空间,并返回指向这块空间的指针。
参数size表示需要开辟的空间字节大小,返回类型为void*,泛型指针,指向被开辟空间首元素地址。具体指向什么类型的数据需要使用者自己决定。
需要注意的是,如果开辟空间失败这个函数会返回NULL,所以为了避免使用空指针,在使用这个函数之后需要判断是否返回了NULL.
free:
void free (void* ptr);
free函数用来释放掉动态开辟的内存。
参数prt表示的是需要释放动态内存空间的指针。
这个函数非常重要,因为如果动态内存分配的空间没有及时释放的话,可能会造成内存泄漏,而长时间运行的程序中存在内存泄漏会逐渐消耗系统的可用内存,最终可能导致程序崩溃或系统变得不稳定。
所以为了避免内存泄漏的情况发生,我们应该养成即使释放动态内存的习惯。
举例:
int main() {//使用malloc动态开辟空间的例子int num = 0;scanf("%d", &num);int* ptr = NULL;ptr = malloc(num * sizeof(int));//开辟了num个整数类型元素大小的空间,返回这片空间的起始位置给ptrif (ptr == NULL) { //判断是否申请空间成功perror("malloc");}//初始化申请空间的元素for (int i = 0; i < num; i++) {*(ptr + i) = i;}//释放空间,防止空间泄漏,指针置空,防止野指针free(ptr);ptr = NULL;return 0;
}
这里我输入的num=10.
监视:

我们发现ptr指针确实指向了10个整型大小的空间,能正常初始化。
释放空间后:

为什么要释放掉空间后将ptr置空?
这是因为虽然我们将动态开辟的空间释放掉了,但是ptr依旧指向这片空间,但是此时ptr已经没有权限操作这片空间了,所以我们需要置空。
1.2 calloc
void* calloc (size_t num, size_t size);
calloc函数与malloc函数相似,都是向内存申请一块连续可用的空间,并返回指向这块空间的指针,区别是,calloc申请的空间里的每个字节会默认初始化为0。
第一个参数num是元素的个数,size表示的是元素的字节大小,开辟num个大小为size的元素的空间,返回开辟空间的起始地址。
其实malloc和calloc的用法也基本一致,只不过比malloc更加省事,不用担心初始化的问题。
举例:
int main() {int* p = (int*)calloc(10, sizeof(int));if (p == NULL) {perror("calloc");//打印错误信息}free(p);p = NULL;return 0;
}
监视指针p:

我们可以看到,在申请到空间后,空间里的元素都被初始化为了0.
1.3 realloc
void* realloc (void* ptr, size_t size);
realloc函数可以做到对动态开辟内存大小的调整,如果此时申请的动态空间不够,那么我们可以使用realloc函数来进行“扩容”。
ptr 是要调整的内存地址,size 调整之后新大小,返回值为调整之后的内存起始位置。 这个函数调整原内存空间大小的基础上,还会将原来内存中的数据移动到新的空间。
realloc申请空间会遇到两种情况:
1.原空间后面有足够空间,会直接在原来ptr指向空间后面追加,返回的地址也是ptr指向的地址。

2.原有空间后面空间不够,那就在堆空间另找一片空间,这个时候返回的的地址就会是一个随机值。

举例:
int main()
{int* ptr = (int*)malloc(120);int ptr2 = ptr;//记录原来ptr指向的地址if (ptr == NULL){perror("malloc");}//扩展容量ptr = (int*)realloc(ptr, 1000);if (ptr == NULL)//如果申请失败返回NULL{perror("realloc");}if (ptr == ptr2) {printf("情况1\n");}else {printf("情况2\n");}free(ptr);ptr = NULL;return 0;
}

我们看到,realloc申请的空间返回的地址不是原来的ptr指向的地址.
我们再来改一下代码:
int main()
{int* ptr = (int*)malloc(120);int ptr2 = ptr;if (ptr == NULL){perror("malloc");}//扩展容量ptr = (int*)realloc(ptr, 140);//扩容的空间很小if (ptr == NULL)//如果申请失败返回NULL{perror("realloc");}if (ptr == ptr2) {printf("情况1\n");}else {printf("情况2\n");}free(ptr);ptr = NULL;return 0;
}
现在我们把realloc申请的空间改小一点,为了能找到符合情况1(realloc在原来ptr指向空间后面追加).

当然这也只是偶然情况,就算是扩容改小一点也不一定就会直接追加空间在ptr指向空间后面。
2.常见的动态内存错误
在介绍了几个动态内存函数的用法之后,我们再来看看在使用动态内存函数时经常犯的错误吧。
2.1 对NULL指针的解引用操作
void test()
{int *p = (int *)malloc(INT_MAX/4);*p = 20;//如果p的值是NULL,就会有问题free(p);
}
如果我们申请的空间太大就有可能开辟空间失败,这个时候指针p接收到的就是NULL,而对NULL解引用操作就会导致错误。
2.2 对动态开辟空间的越界访问
void test()
{int i = 0;int *p = (int *)malloc(10*sizeof(int));if(NULL == p){exit(EXIT_FAILURE);}for(i=0; i<=10; i++){*(p+i) = i;//当i是10的时候越界访问}free(p);
}
我们这里只是申请了10个整型元素大小的空间,这个时候*(p+10)其实已经越界访问了,此时程序就会出错。
2.3 对非动态开辟内存使用free释放
void test()
{int a = 10;int *p = &a;free(p);//ok?
}
上面我们已经知道了free是用来释放动态内存的空间的,而如果对非动态开辟内存使用free释放,等待你的就是这个:

2.4 使用free释放一块动态开辟内存的一部分
void test()
{int *p = (int *)malloc(100);p++;free(p);//p不再指向动态内存的起始位置
}
在执行malloc函数分配内存后,指针p指向动态内存的起始位置。然而,在将指针p增加1后,它不再指向分配的内存起始位置。因此,当在之后使用free函数来释放内存时,由于free函数要求传入指向malloc分配内存起始位置的指针,传入p将导致未定义的行为。这可能会导致程序崩溃或出现其他问题。
2.5 对同一块动态内存多次释放
void test()
{int *p = (int *)malloc(100);free(p);free(p);//重复释放
}
重复释放同一内存块会导致未定义的行为,可能会导致程序崩溃或出现其他问题。
2.6 动态开辟内存忘记释放(内存泄漏)
void test()
{int *p = (int *)malloc(100);if(NULL != p){*p = 20;}
}
int main()
{test();while(1);
}
内存泄漏指的是程序在执行期间申请了一定量的内存空间,但在使用完毕后没有及时释放,导致这部分内存空间永远无法被程序使用,从而浪费了宝贵的系统资源。所以我们一定要及时释放掉申请的空间,这样才能“有借有还再借不难”。
总结
学习动态内存是如何分配的可以让我们以后写出来的程序更加高效灵活,这也是我们作为程序员的基本素养,同时,我们也应该注意正确使用动态内存分配,避免一些常见的错误。
相关文章:
c语言动态内存分布
前言: 随着我们深入的学习c语言,之前使用的静态内存分配已经难以满足我们的实际需求。比如前面我们的通讯录功能的实现,如果只是静态内存分配,那么也就意味着程序开始的内存分配大小就是固定的,应该开多大的空间呢&am…...
1.3.2有理数减法(第一课时)作业设计
【学习目标】 1.理解有理数减法法则,能熟练地进行有理数的减法运算. 2.感受有理数减法与加法对立统一的辨证思想,体会转化的思想方法....
vue3 -- ts封装 Turf.js地图常用方法
Turf.js中文网 地理空间分析库,处理各种地图算法 文档地址 安装 Turf 库 npm install @turf/turf创建src/hooks/useTurf.ts 文件1:获取线中心点 效果: 代码: useTurf.ts import * as turf from @turf/turf// 获取线中心点 export class CenterPointOfLine {...
Qt之实现圆形进度条
在Qt自带的控件中,只有垂直进度条、水平进度条两种。 在平时做页面开发时,有些时候会用到圆形进度条,比如说:下载某个文件的下载进度。 展示效果,如下图所示: 实现这个功能主要由以下几个重点:…...
C# 图解教程 第5版 —— 第1章 C# 和 .NET 框架
文章目录 1.1 在 .NET 之前1.2 .NET 时代1.2.1 .NET 框架的组成1.2.2 大大改进的编程环境 1.3 编译成 CIL1.4 编译成本机代码并执行1.5 CLR1.6 CLI1.7 各种缩写1.8 C# 的演化1.9 C# 和 Windows 的演化(*) 1.1 在 .NET 之前 MFC(Microsoft Fou…...
electronjs入门-聊天应用程序,与Electron.js通信
随着第一章中构建的应用程序,我们将开始将其与Electron框架中的模块集成,并以此为基础,以更实用的方式了解它们。 过程之间的通信 根据第二章中的解释,我们将发送每个进程之间的消息;具体来说联系人和聊天࿱…...
【自用】ubuntu 18.04 LTS安装opencv 3.4.16 + opencv_contrib 3.4.16
1.下载 opencv 3.4.16 opencv_contrib 3.4.16 其中,opencv_contrib解压后的多个文件夹复制到opencv内、合并 声明:尚未验证该方式是否可行 2.安装 参考博文: https://zhuanlan.zhihu.com/p/650792342 https://zhuanlan.zhihu.com/p/8719780…...
递归解析Json,实现生成可视化Tree+快速获取JsonPath | 京东云技术团队
内部平台的一个小功能点的实现过程,分享给大家: 递归解析Json,可以实现生成可视化Tree快速获取JsonPath。 步骤: 1.利用JsonPath读取根,获取JsonObject 2.递归层次遍历JsonObjec,保存结点信息 3.利用z…...
GraceUI相关的 知识
调试工具:UniApp提供了一些调试工具和插件,如uni-app-cli、调试器等,可以帮助你更好地定位和解决问题。同时,使用浏览器的开发者工具或模拟器的调试功能,可以更直观地观察页面效果和调试代码。 对于 GraceUI 的普通版本…...
三十二、【进阶】hash索引结构
1、hash索引结构 (1)简述: hash索引,就是采用一定的hash算法,将键值换算成新的hash值,映射到对应的槽位上,然后存储在hash表中。 (2)图示: 2、hash索引结构…...
如果有一天AI能自主编程了,程序员还有前途吗?
人们一直想知道人工智能(AI)等新技术将如何影响就业。如今的一个大问题是:人工智能会接管程序员的角色吗? 编程主要是关于人们学习计算机语言,这需要大量的时间和努力。但人工智能正在改变这一点。像 GPT-4 这样的系统…...
网络安全:个人信息保护,企业信息安全,国家网络安全的重要性
在当前的数字化时代,无论是个人,企业,还是国家,都会面临严重的网络安全威胁。网络安全不仅涉及我们的日常生活,也涉及到社会的稳定和国家的安全。这就需要我们高度重视网络安全,强化个人信息保护࿰…...
自动驾驶学习笔记(二)——Apollo入门
#Apollo开发者# 学习课程的传送门如下,当您也准备学习自动驾驶时,可以和我一同前往: 《自动驾驶新人之旅》免费课程—> 传送门 《2023星火培训【感知专项营】》免费课程—>传送门 文章目录 前言 Ubuntu Linux文件系统 Linux指令…...
Flask 进行 Web 开发时,常见的错误
ImportError: No module named ‘flask’ 错误描述: 这个错误表示 Python 找不到 Flask 模块。解决方法: 确保已经正确安装了 Flask 模块。你可以使用以下命令来安装 Flask:pip install flaskAttributeError: ‘module’ object has no attri…...
【项目】5.1阻塞和非阻塞、同步和异步 5.2Unix、Linux上的五种IO模型
5.1阻塞和非阻塞、同步和异步(网络IO) 典型的一次IO的两个阶段是什么?数据就绪和数据读写 数据就绪:根据IO操作的就绪状态 阻塞非阻塞 数据读写:根据应用程序和内核的交互方式 同步异步 陈硕:在处理IO的…...
Unity可视化Shader工具ASE介绍——3、ASE的Shader类型介绍
大家好,我是阿赵。这里继续介绍Unity可视化Shader编辑插件ASE的用法。 上一篇介绍了节点的输入输出节点。这一篇来介绍一下不同的Shader类型的区别。 一、修改Shader类型 之前介绍创建Shader的时候,曾经说过可以选择Shader的类型。 其实这个类型是…...
国内手机安装 Google Play 服务 (GMS/Google Mobile Services)
目录 1. 国内手机安装 Google Play 服务 (GMS/Google Mobile Services)1.1. 什么是 GMS1.2. 国内手机只需要安装 3 个 APP1.2.1. Google Services Framework 服务框架1.2.2. Google Play Services1.2.3. Google Play Store 应用商店 1.3. 问题1.3.1. 谷歌地图闪退 2. 小米手机 …...
数据结构与算法-(7)---栈的应用-(4)后缀表达式求值
🌈write in front🌈 🧸大家好,我是Aileen🧸.希望你看完之后,能对你有所帮助,不足请指正!共同学习交流. 🆔本文由Aileen_0v0🧸 原创 CSDN首发🐒 如…...
[VIM]spcaevim
Home | SpaceVim SpaceVim - 知乎 关于Vim/Neovim/SpaceVim的一些思考 - 知乎 vim高配版(1) – SpaceVim 简介 SpaceVim 是国内的一个大佬将一些NB的插件整合到一起的一个插件包. 一键式安装, 功能强大. 官网参见 Home | SpaceVim vim高配版(2) – vimplus 简介 vimplu…...
Android中的RxJava入门及常用操作符
文章目录 1.定义2.作用3.特点4.使用4.1创建被观察者(Observable)4.2创建观察者(Observer)4.3订阅(Subscribe)4.4Dispose 5.操作符5.1操作符类型5.2just操作符5.2链式调用5.3 fromArray操作符5.4 fromIterab…...
K8S认证|CKS题库+答案| 11. AppArmor
目录 11. AppArmor 免费获取并激活 CKA_v1.31_模拟系统 题目 开始操作: 1)、切换集群 2)、切换节点 3)、切换到 apparmor 的目录 4)、执行 apparmor 策略模块 5)、修改 pod 文件 6)、…...
AtCoder 第409场初级竞赛 A~E题解
A Conflict 【题目链接】 原题链接:A - Conflict 【考点】 枚举 【题目大意】 找到是否有两人都想要的物品。 【解析】 遍历两端字符串,只有在同时为 o 时输出 Yes 并结束程序,否则输出 No。 【难度】 GESP三级 【代码参考】 #i…...
【算法训练营Day07】字符串part1
文章目录 反转字符串反转字符串II替换数字 反转字符串 题目链接:344. 反转字符串 双指针法,两个指针的元素直接调转即可 class Solution {public void reverseString(char[] s) {int head 0;int end s.length - 1;while(head < end) {char temp …...
智能仓储的未来:自动化、AI与数据分析如何重塑物流中心
当仓库学会“思考”,物流的终极形态正在诞生 想象这样的场景: 凌晨3点,某物流中心灯火通明却空无一人。AGV机器人集群根据实时订单动态规划路径;AI视觉系统在0.1秒内扫描包裹信息;数字孪生平台正模拟次日峰值流量压力…...
是否存在路径(FIFOBB算法)
题目描述 一个具有 n 个顶点e条边的无向图,该图顶点的编号依次为0到n-1且不存在顶点与自身相连的边。请使用FIFOBB算法编写程序,确定是否存在从顶点 source到顶点 destination的路径。 输入 第一行两个整数,分别表示n 和 e 的值(1…...
从面试角度回答Android中ContentProvider启动原理
Android中ContentProvider原理的面试角度解析,分为已启动和未启动两种场景: 一、ContentProvider已启动的情况 1. 核心流程 触发条件:当其他组件(如Activity、Service)通过ContentR…...
LLaMA-Factory 微调 Qwen2-VL 进行人脸情感识别(二)
在上一篇文章中,我们详细介绍了如何使用LLaMA-Factory框架对Qwen2-VL大模型进行微调,以实现人脸情感识别的功能。本篇文章将聚焦于微调完成后,如何调用这个模型进行人脸情感识别的具体代码实现,包括详细的步骤和注释。 模型调用步骤 环境准备:确保安装了必要的Python库。…...
STM32标准库-ADC数模转换器
文章目录 一、ADC1.1简介1. 2逐次逼近型ADC1.3ADC框图1.4ADC基本结构1.4.1 信号 “上车点”:输入模块(GPIO、温度、V_REFINT)1.4.2 信号 “调度站”:多路开关1.4.3 信号 “加工厂”:ADC 转换器(规则组 注入…...
门静脉高压——表现
一、门静脉高压表现 00:01 1. 门静脉构成 00:13 组成结构:由肠系膜上静脉和脾静脉汇合构成,是肝脏血液供应的主要来源。淤血后果:门静脉淤血会同时导致脾静脉和肠系膜上静脉淤血,引发后续系列症状。 2. 脾大和脾功能亢进 00:46 …...
【Java】Ajax 技术详解
文章目录 1. Filter 过滤器1.1 Filter 概述1.2 Filter 快速入门开发步骤:1.3 Filter 执行流程1.4 Filter 拦截路径配置1.5 过滤器链2. Listener 监听器2.1 Listener 概述2.2 ServletContextListener3. Ajax 技术3.1 Ajax 概述3.2 Ajax 快速入门服务端实现:客户端实现:4. Axi…...
