当前位置: 首页 > news >正文

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语言动态内存分布

前言&#xff1a; 随着我们深入的学习c语言&#xff0c;之前使用的静态内存分配已经难以满足我们的实际需求。比如前面我们的通讯录功能的实现&#xff0c;如果只是静态内存分配&#xff0c;那么也就意味着程序开始的内存分配大小就是固定的&#xff0c;应该开多大的空间呢&am…...

1.3.2有理数减法(第一课时)作业设计

【学习目标】 1&#xff0e;理解有理数减法法则&#xff0c;能熟练地进行有理数的减法运算&#xff0e; 2&#xff0e;感受有理数减法与加法对立统一的辨证思想&#xff0c;体会转化的思想方法&#xff0e;...

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自带的控件中&#xff0c;只有垂直进度条、水平进度条两种。 在平时做页面开发时&#xff0c;有些时候会用到圆形进度条&#xff0c;比如说&#xff1a;下载某个文件的下载进度。 展示效果&#xff0c;如下图所示&#xff1a; 实现这个功能主要由以下几个重点&#xff1a…...

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 的演化&#xff08;*&#xff09; 1.1 在 .NET 之前 MFC&#xff08;Microsoft Fou…...

electronjs入门-聊天应用程序,与Electron.js通信

随着第一章中构建的应用程序&#xff0c;我们将开始将其与Electron框架中的模块集成&#xff0c;并以此为基础&#xff0c;以更实用的方式了解它们。 过程之间的通信 根据第二章中的解释&#xff0c;我们将发送每个进程之间的消息&#xff1b;具体来说联系人和聊天&#xff1…...

【自用】ubuntu 18.04 LTS安装opencv 3.4.16 + opencv_contrib 3.4.16

1.下载 opencv 3.4.16 opencv_contrib 3.4.16 其中&#xff0c;opencv_contrib解压后的多个文件夹复制到opencv内、合并 声明&#xff1a;尚未验证该方式是否可行 2.安装 参考博文&#xff1a; https://zhuanlan.zhihu.com/p/650792342 https://zhuanlan.zhihu.com/p/8719780…...

递归解析Json,实现生成可视化Tree+快速获取JsonPath | 京东云技术团队

内部平台的一个小功能点的实现过程&#xff0c;分享给大家&#xff1a; 递归解析Json&#xff0c;可以实现生成可视化Tree快速获取JsonPath。 步骤&#xff1a; 1.利用JsonPath读取根&#xff0c;获取JsonObject 2.递归层次遍历JsonObjec&#xff0c;保存结点信息 3.利用z…...

GraceUI相关的 知识

调试工具&#xff1a;UniApp提供了一些调试工具和插件&#xff0c;如uni-app-cli、调试器等&#xff0c;可以帮助你更好地定位和解决问题。同时&#xff0c;使用浏览器的开发者工具或模拟器的调试功能&#xff0c;可以更直观地观察页面效果和调试代码。 对于 GraceUI 的普通版本…...

三十二、【进阶】hash索引结构

1、hash索引结构 &#xff08;1&#xff09;简述&#xff1a; hash索引&#xff0c;就是采用一定的hash算法&#xff0c;将键值换算成新的hash值&#xff0c;映射到对应的槽位上&#xff0c;然后存储在hash表中。 &#xff08;2&#xff09;图示&#xff1a; 2、hash索引结构…...

如果有一天AI能自主编程了,程序员还有前途吗?

人们一直想知道人工智能&#xff08;AI&#xff09;等新技术将如何影响就业。如今的一个大问题是&#xff1a;人工智能会接管程序员的角色吗&#xff1f; 编程主要是关于人们学习计算机语言&#xff0c;这需要大量的时间和努力。但人工智能正在改变这一点。像 GPT-4 这样的系统…...

网络安全:个人信息保护,企业信息安全,国家网络安全的重要性

在当前的数字化时代&#xff0c;无论是个人&#xff0c;企业&#xff0c;还是国家&#xff0c;都会面临严重的网络安全威胁。网络安全不仅涉及我们的日常生活&#xff0c;也涉及到社会的稳定和国家的安全。这就需要我们高度重视网络安全&#xff0c;强化个人信息保护&#xff0…...

自动驾驶学习笔记(二)——Apollo入门

#Apollo开发者# 学习课程的传送门如下&#xff0c;当您也准备学习自动驾驶时&#xff0c;可以和我一同前往&#xff1a; 《自动驾驶新人之旅》免费课程—> 传送门 《2023星火培训【感知专项营】》免费课程—>传送门 文章目录 前言 Ubuntu Linux文件系统 Linux指令…...

Flask 进行 Web 开发时,常见的错误

ImportError: No module named ‘flask’ 错误描述&#xff1a; 这个错误表示 Python 找不到 Flask 模块。解决方法&#xff1a; 确保已经正确安装了 Flask 模块。你可以使用以下命令来安装 Flask&#xff1a;pip install flaskAttributeError: ‘module’ object has no attri…...

【项目】5.1阻塞和非阻塞、同步和异步 5.2Unix、Linux上的五种IO模型

5.1阻塞和非阻塞、同步和异步&#xff08;网络IO&#xff09; 典型的一次IO的两个阶段是什么&#xff1f;数据就绪和数据读写 数据就绪&#xff1a;根据IO操作的就绪状态 阻塞非阻塞 数据读写&#xff1a;根据应用程序和内核的交互方式 同步异步 陈硕&#xff1a;在处理IO的…...

Unity可视化Shader工具ASE介绍——3、ASE的Shader类型介绍

大家好&#xff0c;我是阿赵。这里继续介绍Unity可视化Shader编辑插件ASE的用法。   上一篇介绍了节点的输入输出节点。这一篇来介绍一下不同的Shader类型的区别。 一、修改Shader类型 之前介绍创建Shader的时候&#xff0c;曾经说过可以选择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)后缀表达式求值

&#x1f308;write in front&#x1f308; &#x1f9f8;大家好&#xff0c;我是Aileen&#x1f9f8;.希望你看完之后&#xff0c;能对你有所帮助&#xff0c;不足请指正&#xff01;共同学习交流. &#x1f194;本文由Aileen_0v0&#x1f9f8; 原创 CSDN首发&#x1f412; 如…...

[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创建被观察者&#xff08;Observable&#xff09;4.2创建观察者&#xff08;Observer&#xff09;4.3订阅&#xff08;Subscribe&#xff09;4.4Dispose 5.操作符5.1操作符类型5.2just操作符5.2链式调用5.3 fromArray操作符5.4 fromIterab…...

LBE-LEX系列工业语音播放器|预警播报器|喇叭蜂鸣器的上位机配置操作说明

LBE-LEX系列工业语音播放器|预警播报器|喇叭蜂鸣器专为工业环境精心打造&#xff0c;完美适配AGV和无人叉车。同时&#xff0c;集成以太网与语音合成技术&#xff0c;为各类高级系统&#xff08;如MES、调度系统、库位管理、立库等&#xff09;提供高效便捷的语音交互体验。 L…...

论文解读:交大港大上海AI Lab开源论文 | 宇树机器人多姿态起立控制强化学习框架(二)

HoST框架核心实现方法详解 - 论文深度解读(第二部分) 《Learning Humanoid Standing-up Control across Diverse Postures》 系列文章: 论文深度解读 + 算法与代码分析(二) 作者机构: 上海AI Lab, 上海交通大学, 香港大学, 浙江大学, 香港中文大学 论文主题: 人形机器人…...

<6>-MySQL表的增删查改

目录 一&#xff0c;create&#xff08;创建表&#xff09; 二&#xff0c;retrieve&#xff08;查询表&#xff09; 1&#xff0c;select列 2&#xff0c;where条件 三&#xff0c;update&#xff08;更新表&#xff09; 四&#xff0c;delete&#xff08;删除表&#xf…...

C++:std::is_convertible

C++标志库中提供is_convertible,可以测试一种类型是否可以转换为另一只类型: template <class From, class To> struct is_convertible; 使用举例: #include <iostream> #include <string>using namespace std;struct A { }; struct B : A { };int main…...

关于iview组件中使用 table , 绑定序号分页后序号从1开始的解决方案

问题描述&#xff1a;iview使用table 中type: "index",分页之后 &#xff0c;索引还是从1开始&#xff0c;试过绑定后台返回数据的id, 这种方法可行&#xff0c;就是后台返回数据的每个页面id都不完全是按照从1开始的升序&#xff0c;因此百度了下&#xff0c;找到了…...

【第二十一章 SDIO接口(SDIO)】

第二十一章 SDIO接口 目录 第二十一章 SDIO接口(SDIO) 1 SDIO 主要功能 2 SDIO 总线拓扑 3 SDIO 功能描述 3.1 SDIO 适配器 3.2 SDIOAHB 接口 4 卡功能描述 4.1 卡识别模式 4.2 卡复位 4.3 操作电压范围确认 4.4 卡识别过程 4.5 写数据块 4.6 读数据块 4.7 数据流…...

对WWDC 2025 Keynote 内容的预测

借助我们以往对苹果公司发展路径的深入研究经验&#xff0c;以及大语言模型的分析能力&#xff0c;我们系统梳理了多年来苹果 WWDC 主题演讲的规律。在 WWDC 2025 即将揭幕之际&#xff0c;我们让 ChatGPT 对今年的 Keynote 内容进行了一个初步预测&#xff0c;聊作存档。等到明…...

【android bluetooth 框架分析 04】【bt-framework 层详解 1】【BluetoothProperties介绍】

1. BluetoothProperties介绍 libsysprop/srcs/android/sysprop/BluetoothProperties.sysprop BluetoothProperties.sysprop 是 Android AOSP 中的一种 系统属性定义文件&#xff08;System Property Definition File&#xff09;&#xff0c;用于声明和管理 Bluetooth 模块相…...

【单片机期末】单片机系统设计

主要内容&#xff1a;系统状态机&#xff0c;系统时基&#xff0c;系统需求分析&#xff0c;系统构建&#xff0c;系统状态流图 一、题目要求 二、绘制系统状态流图 题目&#xff1a;根据上述描述绘制系统状态流图&#xff0c;注明状态转移条件及方向。 三、利用定时器产生时…...

在Ubuntu24上采用Wine打开SourceInsight

1. 安装wine sudo apt install wine 2. 安装32位库支持,SourceInsight是32位程序 sudo dpkg --add-architecture i386 sudo apt update sudo apt install wine32:i386 3. 验证安装 wine --version 4. 安装必要的字体和库(解决显示问题) sudo apt install fonts-wqy…...