【C语言】动态内存管理:malloc、calloc、realloc、free

 本篇介绍一下C语言中的malloc/calloc/realloc。 使用这些函数需要包含头文件<stdlib.h>。malloc/calloc/realloc申请的空间都是 堆区的。

1.malloc和free

1.1 malloc

C语言提供了一个动态内存开辟的函数malloc,函数原型如下。

void* malloc(size_t size); //size的单位是字节

这个函数向内存申请一块连续可用的空间,并返回指向这款空间的指针。

如果开辟成功,则返回一个指向开辟好空间的指针。

如果开辟失败,则返回NULL,因此malloc的返回值一定要做检查

返回值类型是void*,所以malloc函数并不知道开辟空间的类型,具体要在使用的时候自己来决定。要强制类型转换。

y如果参数size为0,malloc的行为是标准未定义的,取决于编译器。

 比如我们申请20个字节,用来存放整形。下面两种写法是一样的。

int* p0 = (int*)malloc(20);
int* p1 = (int*)malloc(5 * sizeof(int));

然后检查一下返回值。

int* p0 = (int*)malloc(20);
//检查返回值
if (p0 == NULL) //申请失败
{perror("malloc fail"); return 1;
}

申请成功就可以使用空间了。当作数组使用就行。

 然后我们往里面存一些值进去

1.2 free

申请的空间不要了,还要手动换回去。C语言提供了另一个函数free,专门是用来做动态内存释放和回收的,函数原型如下。

void free(void* ptr);

free函数用来释放动态开辟内存。ptr传的是要释放的内存空间的起始地址

如果参数ptr指向的空间不是动态开辟的,那么free的行为是未定义的。

如果参数ptr是NULL指针,则函数什么都不做。 

拿前面的p0举例,我们现在要释放它。

free(p0); //把空间还给操作系统

 

此时p0指针什么都没指,是个野指针,所以我们还要手动把p0置为NULL,这一步千万不能忘。

free(p0); //把空间还给操作系统
p0 = NULL; //置空

 所以我们在使用申请的空间的时候,要记得记录起始位置指针,最好就是别改变起始位置指针的指向。

2.calloc和realloc

2.1 calloc

C语言还提供了一个函数叫做calloc,这个函数也是用来动态内存分配的,原型如下。

void* calloc(size_t num, size_t size);

 函数功能是为num个大小为size的元素开辟空间,并且把空间的每个元素初始化为0

与malloc函数的区别只在于calloc会在返回地址之前把申请的空间的每个字节初始化为全0。

比如我们想向内存申请5个整形的空间。

int* p1 = (int*)calloc(5, 4);
int* p1 = (int*)calloc(5, sizeof(int)); //这样写也可以

同样的,要检查返回值。

int* p1 = (int*)calloc(5, sizeof(int));
if (p1 == NULL)
{perror("calloc fail");return 1;
}

我们通过调试看p的内存,发现都初始化为了0。 

 除了calloc能初始化,其他的和malloc没什么区别。

最后也是一样,不用了就free,并且把p1置空。

free(p1);
p1 = NULL;

2.2 realloc

realloc的出现让动态内存管理更加灵活,它可以调整动态开辟内存的大小,函数原型如下。

void* realloc(void* ptr, size_t size);

ptr是要调整的内存地址,size是调整之后新的大小,返回值为调整之后的内存起始位置。

比如说我要把前面的p0申请的20个空间扩展到40个。有下面的两种情况。

情况一:直接在原来的基础上往后扩展就行,然后返回起始地址。 

情况二 :

        1.在内存里重新找一块区域,这块区域直接满足40个字节的需求。

        2.把原来的内容都拷贝在这个新内存里。

        3.释放旧空间。(realloc主动释放,不用自己手动释放)

        4.返回新的内存空间的起始地址。

情况三:新找的空间也不够,直接返回NULL。

所以我们在使用realloc的时候就不能直接像下面这样写。

int* p0 = (int*)malloc(20);
if (p0 == NULL) 
{perror("malloc fail");return 1;
}
for (int i = 0; i < 5; i++)
{*(p0 + i) = i;
}int* p0 = (int*)realloc(p0, 40); //错误写法

因为一旦扩容失败,返回NULL,把NULL放在p0里,不但没有调整空间,原来的数据也丢失了。所以不能直接用原来的指针直接接受扩容后的新地址,要先进行一个判断,如下。

int* newptr = (int*)realloc(p0, 40);//新指针接收
if (newptr != NULL) //判断是否成功
{p0 = newptr; //成功了再用原来的指针接管扩容后的空间
}
else //失败
{perror("realloc fail"); //...
}

 不用了同样要free释放。

realloc函数的第一个参数如果传NULL指针的话,可以实现和malloc一样的功能。

realloc(NULL, 20); //== malloc(20);

3.常见的动态内存错误

3.1 对NULL指针解引用

void test1()
{//INT_MAX是int最大值,此时空间开辟绝对是失败的int* p = (int*)malloc(INT_MAX); *p = 20;  //开辟空间失败返回NULL,不能对NULL解引用free(p);p = NULL;
}

所以我们一定要判断malloc的返回值。 

3.2 对动态开辟的空间越界访问

void test2()
{int i = 0;int* p = (int*)malloc(10 * sizeof(int));if (p == NULL){return;}for (i = 0; i <= 10; i++){*(p + i) = i;  //当i为10时越界访问}free(p);p = NULL;
}

malloc申请的空间和数组非常相似,不可以越界访问。

3.3 对非动态开辟的内存使用free释放

void test3()
{int a = 10;int* p = &a;free(p);p = NULL;
}

不是动态开辟的空间不需要free,int的空间在栈上,也不在堆上,动态申请的才在堆区上。free只释放动态开辟的。

3.4 使用free释放一块动态开辟内存的一部分

void test4()
{int* p = (int*)malloc(100);p++;free(p); //p此时不再指向起始位置p = NULL;
}

free(ptr)我们前面说过ptr必须是起始位置。只释放一部分程序会出错崩溃。

3.5 对同一块内存多次释放

void test5()
{int* p = malloc(100);free(p);free(p); //不可重复释放
}

这种情况最好避免发生的方法就是free之后赶紧置空。

void test5()
{int* p = malloc(100);free(p);p = NULL;free(p); //free空,什么都不发生
}

 

3.6 动态开辟的内存忘记释放

void test6()
{int* p = (int*)malloc(100);if (NULL != p){*p = 20;}
}
int main()
{test6();//出test6函数后p就销毁了,想释放现在也释放不了while (1);//程序死循环return 0;
}

自己已经不用了,不释放不还给操作系统,别人也不能用,会发生内存泄漏问题。

但是我们不用free释放,程序运行结束的时候,也会由操作系统回收,如果程序一直不退出呢?所以最好别忘记释放。

本篇分享就到这里,拜拜~

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

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【docker学习】Linux系统离线方式安装docker环境方法

centos7-linux安装docker(离线方式) 下载docker的安装文件 https://download.docker.com/linux/static/stable/x86_64/ 下载的是&#xff1a;docker-18.06.3-ce.tgz 这个压缩文件 将docker-18.06.3-ce.tgz文件上传到centos7-linux系统上&#xff0c;用ftp工具上传即可 解压…

移情别恋c++ ദ്ദി˶ー̀֊ー́ ) ——15.红黑树

1.红黑树的概念 红黑树&#xff0c;是一种二叉搜索树&#xff0c;但在每个结点上增加一个存储位表示结点的颜色&#xff0c;可以是Red或 Black。 通过对任何一条从根到叶子的路径上各个结点着色方式的限制&#xff0c;红黑树确保没有一条路 径会比其他路径长出俩倍&#xff0c;…

【C++】类与对象(一)

「前言」 &#x1f308;个人主页&#xff1a; 代码探秘者 &#x1f308;C语言专栏&#xff1a;C语言 &#x1f308;C专栏&#xff1a; C &#x1f308;喜欢的诗句:天行健,君子以自强不息. 目录 [一、面向过程和面向对象初步认识][Link 2] 二、类的引入 三、类的定义 四、类…

李宏毅机器学习2022-HW8-Anomaly Detection

文章目录 TaskBaselineReportQuestion2 Code Link Task 异常检测Anomaly Detection 将data经过Encoder&#xff0c;在经过Decoder&#xff0c;根据输入和输出的差距来判断异常图像。training data是100000张人脸照片&#xff0c;testing data有大约10000张跟training data相同…

值传递和引用传递

值传递和引用传递是函数参数传递的两种类型&#xff0c;一般而言&#xff0c;基本数据类型都是值传递&#xff0c;数组和对象采用引用传递减少对象复制开销&#xff0c;但也有特例。 值和引用传递本质一样 值传递是拷贝值到函数参数&#xff0c;引用传递是拷贝引用(或者对象的指…

第十三届蓝桥杯真题Java c组C.纸张尺寸(持续更新)

博客主页&#xff1a;音符犹如代码系列专栏&#xff1a;蓝桥杯关注博主&#xff0c;后期持续更新系列文章如果有错误感谢请大家批评指出&#xff0c;及时修改感谢大家点赞&#x1f44d;收藏⭐评论✍ 【问题描述】 在 ISO 国际标准中定义了 A0 纸张的大小为 1189mm 841mm&#…

jenkins项目发布基础

随着软件开发需求及复杂度的不断提高,团队开发成员之间如何更好地协同工作以确保软件开发的质量已经慢慢成为开发过程中不可回避的问题。Jenkins 自动化部署可以解决集成、测试、部署等重复性的工作,工具集成的效率明显高于人工操作;并且持续集成可以更早的获取代码变更的信息,…

博弈论(学习笔记)

定义何为最优&#xff01; 最优解是均衡&#xff01;&#xff08;&#xff09; 一次博弈 --- 一面之缘 复杂动态博弈&#xff1b; 路怒症----陌生人&#xff0c;一次性博弈。 一次性博弈最能暴露人性。 重复博弈太压抑了。 沙普利求解合作博弈的著名理论---如何为参与者制定利益…

SQL第11课——使用子查询

11.1 子查询 查询&#xff08;query): 任何SQL语句都是查询&#xff0c;但此术语一般指select语句。 select语句是SQL的查询&#xff0c;迄今为止所有的select语句都是简单查询&#xff0c;即从单个数据库表中检索数据的单条语句。 SQL还允许创建子查询&#xff08;subquer…

浪 得 虚 名 ?RAG接入知识图谱!全局数据关系表示!指哪打哪!

在学知识图谱系列的朋友就知道&#xff01; 知识图谱RAG&#xff01;最大优势&#xff1a; 全局数据关系表示&#xff01; 相似文本散落在–>不规则不相邻文本块&#xff01; 传统RAG只能找到top-k个文本块&#xff0c;一旦文本块过多&#xff0c;超出llm上下文限制&…

uni-app canvas文本自动换行

封装 支持单行文本超出换行。多行文本顺位排版 // 填充自动换行的文本function fillFeedText({ctx, text, x, y, maxWidth, lineHeight, color, size}) {// 文本配置ctx.setFontSize(size);ctx.setFillStyle(color);// 计算文本换行宽高&#xff0c;换行逻辑const words text…

C++里的随机数

想用C做最基础的猜数字,肯定少不了随机数; srand(unsigned(time(NULL))); rand() //是生成一个随机数 rand()%1001//就是一个从一到一百的随机数 合体: #include <iostream> #include <cstdlib> #include <time.h> int main() { int g 0; while (g < …

虚拟机、ubantu不能连接网络,解决办法

虚拟机、ubantu不能连接网络&#xff0c;解决办法 物理机OS&#xff1a; [Windows10 专业版](https://so.csdn.net/so/search?qWindows10 专业版&spm1001.2101.3001.7020) 虚拟机平台&#xff1a; VMware Workstation 16 Pro 虚拟机OS&#xff1a; Ubuntu 18.04 自动配…

pip install kaggle-environments ISSUE:Failed to build vec-noise

ISSUE: error: Microsoft Visual C 14.0 or greater is required. Get it with “Microsoft C Build Tools”: https://visualstudio.microsoft.com/visual-cpp-build-tools/ [end of output]Failed to build vec-noiseC:\ProgramData\miniconda3\include\pyconfig.h(59): fat…

新农人的求索:既要种菜,也要种钱

澎湃新闻记者 何惠子 灯下立着一个玻璃瓶&#xff0c;内里空无一物&#xff0c;清晰透亮。 一只手握住瓶身。“就像这个瓶子。前途一片光明&#xff0c;但其实都在瓶子里。” 解晓巍说的是音乐——他曾梦想以此维生。事实上&#xff0c;这也适合描述农业。 在没有任何收入的202…

XFTP-8下载安装教程

下载地址 https://www.xshell.com/zh/free-for-home-school/ 新建XFTP文件夹 安装过程 选择新建的文件夹 此处默认即可 填写信息提交注册 点击生成的链接 点击后来&#xff0c;完成安装

防止电脑电池老化,禁止usb或者ac接口调试时充电

控制android系统&#xff0c;开发者模式&#xff0c;开启和禁止充电 连接 Android 手机到电脑的 USB 端口。 下载并安装 Android Debug Bridge (ADB) 工具[1]。 USB&#xff1a; 在命令行中输入 adb shell dumpsys battery set usb 0&#xff0c;以禁止 USB 充电。 在命令…

2024年健康经济与大数据研讨会(HEBD 2024)2024 Seminar on Health Economics and Big Data

在线投稿&#xff1a;学术会议-学术交流征稿-学术会议在线-艾思科蓝 2024年经济决策与人工智能国际学术会议 &#xff08;EDAI 2024&#xff09;将在2024年11月08-10日在广东省广州市隆重举行。大会邀请来自国内外高等院校、科学研究所、企事业单位的专家、教授、学者、工程师…

线程池:线程池的实现 | 日志

&#x1f308;个人主页&#xff1a; 南桥几晴秋 &#x1f308;C专栏&#xff1a; 南桥谈C &#x1f308;C语言专栏&#xff1a; C语言学习系列 &#x1f308;Linux学习专栏&#xff1a; 南桥谈Linux &#x1f308;数据结构学习专栏&#xff1a; 数据结构杂谈 &#x1f308;数据…

spark计算引擎-架构和应用

一Spark 定义&#xff1a;Spark 是一个开源的分布式计算系统&#xff0c;它提供了一个快速且通用的集群计算平台。Spark 被设计用来处理大规模数据集&#xff0c;并且支持多种数据处理任务&#xff0c;包括批处理、交互式查询、机器学习、图形处理和流处理。 核心架构&#x…