你一定想知道的 如何进行动态内存管理?

news2024/9/22 5:29:40

文章目录

  • 引言
  • malloc函数
  • calloc函数
  • realloc函数
  • free函数-避免内存泄漏
  • 常见的动态内存错误

引言

如果我们被问道:如何创建一个可以根据用户需求来开辟大小的数组?

可能有些博友会写出如下代码:

#include <stdio.h>
int main()
{
	int n = 0;
	scanf("%d", &n);
	int arr[n];
	return 0;
}

这个代码在C99标准下是可以运行的,但大多数编译器并不支持C99标准,所以这种代码缺乏了跨平台性(可移植性),那么我们有没有办法写出一个既可以满足题目要求,又可以在任何一个编译器下都编译得过去的代码呢?

答案是肯定的。这就和C语言中的动态内存的开辟有关了,动态开辟,即可以按照需求开辟内存的大小。

下面,我向博友们介绍几个操作动态内存的常用函数。

注:下面介绍的几个函数的操作对象都是堆区的内存。

扩展:局部变量存放在内存中的栈区;全局变量、静态变量(static修饰的变量)存放在内存中的静态区(也叫数据段)。

malloc函数

void *malloc( size_t size );

malloc函数的功能是开辟指定字节大小的内存空间,如果开辟成功就返回该空间的首地址,如果开辟失败就返回一个NULL。传参时只需传入需要开辟的字节个数。

假设我们要开辟一个可以存放10个整型的空间:

#include <stdio.h>
#include <stdlib.h>
int main()
{
	int* p = (int*)malloc(10 * sizeof(int));
	//因为malloc函数的返回值为void*,所以需要强制类型转换为对应类型。
	if (p == NULL)
	{
		printf("内存开辟失败\n");
	}
	else
	{
		printf("内存开辟成功\n");
		//使用...
		//使用结束,释放内存(后面介绍)
		free(p);
		p = NULL;
	}
	return 0;
}

注:malloc函数开辟好空间后,不对空间内容做任何初始化,所以空间内的数据为随机值。

calloc函数

void *calloc( size_t num, size_t size );

calloc函数的功能也是开辟指定大小的内存空间,如果开辟成功就返回该空间的首地址,如果开辟失败就返回一个NULL。但calloc函数传参时需要传入两个参数(开辟的内存用于存放的元素个数和每个元素的大小)。

calloc函数与malloc函数的用法也是大同小异:

#include <stdio.h>
#include <stdlib.h>
int main()
{
	int* p = (int*)calloc(10 , sizeof(int));
	//开辟一个可以存放10个整型的内存空间
	if (p == NULL)
	{
		printf("内存开辟失败\n");
	}
	else
	{
		printf("内存开辟成功\n");
		//使用...
		//使用结束,释放内存(后面介绍)
		free(p);
		p = NULL;
	}
	return 0;
}

注:calloc函数与malloc函数的最大区别在于:calloc函数开辟好内存后会将空间内容中的每一个字节都初始化为0。

realloc函数

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

realloc函数可以调整已经开辟好的动态内存的大小,第一个参数是需要调整大小的动态内存的首地址,第二个参数是动态内存调整后的新大小。

realloc函数与上面两个函数一样,如果开辟成功便返回开辟好的内存的首地址,开辟失败则返回NULL。

如果我们要将已开辟的动态内存大小做一些调整,我们会这样做:

#include <stdio.h>
#include <stdlib.h>
int main()
{
	int* p = (int*)calloc(10, sizeof(int));
	if (p == NULL)
	{
		printf("内存开辟失败\n");
	}
	else
	{
		printf("内存开辟成功\n");
		//使用...
		//扩展容量
		int* ptr = (int*)realloc(p, 100);
		//将空间扩展为100个字节大小
		if (ptr != NULL)
		{
			p = ptr;//开辟成功时
			//使用...
		}
		//使用结束,释放内存(后面介绍)
		free(p);
		p = NULL;
	}
	return 0;
}

realloc函数调整动态内存大小的时候会有三种情况:

假如我们要将一个大小为50个字节的空间调整为100个字节。

情况一:

在这里插入图片描述

此时,realloc函数直接在原空间后方进行扩展,并返回该内存空间首地址(即原来的首地址)。

情况二:

在这里插入图片描述

此时,realloc函数会在堆区中重新找一块满足要求的内存空间,把原空间内的数据拷贝到新空间中,并主动将原空间内存释放(即还给操作系统),返回新内存空间的首地址。

情况三:

在这里插入图片描述

此时,需扩展的空间后方没有足够的空间可供扩展,并且堆区中也没有符合需要开辟的内存大小的空间。结果就是开辟内存失败,返回一个NULL。

free函数-避免内存泄漏

void free( void *memblock );

free函数的作用就是将malloc、calloc以及realloc函数申请的动态内存空间释放,其释放空间的大小取决于之前申请的内存空间的大小。

其使用方式非常简单,就是在使用完后加上两个语句:

	free(p);//p为要释放的代码块的首地址
	p = NULL;//必不可少

我们也已经看到了,上面每一个开辟了动态内存的代码,在使用完该动态内存后,都将该内存空间释放了(即还给操作系统)。

如果在使用完动态内存后忘记将其空间释放,便会造成内存泄漏的问题:

1>
内存泄漏(MemoryLeak)是指程序中已动态分配的堆内存由于某种原因程序未释放或无法释放,造成系统内存的浪费,导致程序运行速度减慢甚至系统崩溃等严重后果。

2>
内存泄漏缺陷具有隐蔽性、积累性的特征,比其他内存非法访问错误更难检测。因为内存泄漏的产生原因是内存块未被释放,属于遗漏型缺陷而不是过错型缺陷。此外,内存泄漏通常不会直接产生可观察的错误症状,而是逐渐积累,降低系统整体性能,极端的情况下可能使系统崩溃。

注意:
1> 在释放代码块后,必须将该代码块的首地址改为NULL,否则该指针将变为野指针,我们都知道,野指针非常危险。
2> 如果传入free函数的为空指针(NULL),则free函数什么也不做。

常见的动态内存错误

一、对NULL指针进行解引用操作

我们都知道,不能对NULL指针进行解引用操作,但是如果我们在调用了malloc、calloc以及realloc函数之后,没有检测返回的指针的有效性(即是否为NULL指针),那我们在后面使用该指针的时候就可能会导致对NULL指针进行解引用操作。

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

我们需要时刻注意,不能访问未申请的动态内存空间。比如你向动态内存申请了10个字节,那就绝不能访问第11个字节。

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

free函数只能释放动态开辟的内存空间。

四、使用free释放动态开辟内存的一部分

free函数只能从开辟好的动态内存空间的起始位置开始释放,所以使用free函数释放动态内存时,传入的指针必须是当时开辟内存时返回的指针。

五、对同一块内存多次释放

对同一块动态内存空间只能释放一次。避免这个问题的出现也很简单,我们只要记住在第一次释放完空间后立即将该指针置为NULL即可,因为当传入free函数的指针为NULL指针时,free函数什么也不做(也就不会出现对同一内存多次释放的问题)。

六、动态开辟内存忘记释放(内存泄漏)

一定要做到自己开辟的动态内存自己记得释放,也许你觉得这件事没什么,但当你需要从几十万甚至几百万行代码中找出一个因忘记释放动态内存而造成的内存泄漏问题时,你就会真正知道这件事的重要性。

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

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

相关文章

c++11 标准模板(STL)(std::basic_streambuf)(二)

定义于头文件 <streambuf> template< class CharT, class Traits std::char_traits<CharT> > class basic_streambuf; 类 basic_streambuf 控制字符序列的输入与输出。它包含下列内容并提供到它们的访问&#xff1a; 1) 受控制字符序列&#xff…

专项练习9

目录 一、选择题 1、在 JavaScript 中&#xff0c;用于阻止默认事件的默认操作的方法是 2、以下代码执行后&#xff0c;result 的值为&#xff08;&#xff09; 3、不能从字符串 const str qwbewrbbeqqbbbweebbbbqee;中能得到结果 ["b", "bb", "bbb…

实时在线云消费机、考勤门禁控制器、网络读卡器服务端C# Socket源码

消费机UDP通讯协议介绍&#xff1a; 设备向服务器发送的指令格式&#xff0c;每个字段用半角逗号(,)分隔。序号指令名称指令格式指令说明示例1响应服务器的搜索100,包序列号,终端IP,子网掩码,网关IP,远程电脑主机IP,端口号,终端硬件号响应电脑发出的搜寻局域网内所有终端设备指…

【Python 基础篇】Python 异常处理

文章目录 引言一、Python异常概述二、常见的内置异常三、异常处理语句四、异常捕获和处理五、实例演示六、总结 引言 在软件开发中&#xff0c;错误和异常是难以避免的。当我们编写Python代码时&#xff0c;有时候会遇到各种各样的问题&#xff0c;例如无效的输入、文件不存在…

hello算法笔记之树

一、二叉树 与链表类似&#xff0c;二叉树的基本单元是节点&#xff0c;每个节点包含一个「值」和两个「指针」。 在二叉树中&#xff0c;除叶节点外&#xff0c;其他所有节点都包含子节点和非空子树。 一些术语&#xff1a; 「根节点 Root Node」&#xff1a;位于二叉树顶…

VNC虚拟网络控制台(概述、windows系统连接linux系统演示)

第三阶段基础 时 间&#xff1a;2023年6月22日 参加人&#xff1a;全班人员 内 容&#xff1a; VNC虚拟网络控制台 目录 一、VNC概述 二、VNC基本上是由两部分组成 三、VNC特点 四、工作流程 五、安装 六、操作演示Windiws10系统远程控制linux 服务端&#xff1a;…

window版安装kafka并提供启动快捷脚本

kafka下载地址&#xff1a; 链接&#xff1a;https://pan.baidu.com/s/1DpcGXvpTYAcG_fvS-p9-3g?pwd1234 提取码&#xff1a;1234 官网&#xff1a;https://kafka.apache.org/downloads 注意不需要单独安装zk&#xff0c;里面包括zk Kafka解压包目录不要太深了&#xff0c…

养老院人员跌倒检测识别算法

养老院人员跌倒检测识别预警系统通过yolov5python网络模型技术&#xff0c;养老院人员跌倒检测识别预警算法对跌倒事件进行识别和分析&#xff0c;当检测到有人员跌倒时&#xff0c;将自动发出警报提示相关人员及时采取措施。YOLOv5是一种单阶段目标检测算法&#xff0c;该算法…

CTF-Show密码学【Base64、栅栏密码、16进制】

题目内容 密文&#xff1a;53316C6B5A6A42684D3256695A44566A4E47526A4D5459774C5556375A6D49324D32566C4D4449354F4749345A6A526B4F48303D 提交格式&#xff1a;KEY{XXXXXXXXXXXXXX}工具下载&#xff1a;https://www.lanzoui.com/i9fn2aj萌新_密码13 分析和解决过程 初步分析…

【Python 基础篇】Python 面向对象编程:理解与实践

文章目录 一、引言二、类与对象三、封装与访问控制四、继承与多态&#xff08;第一部分&#xff09;五、方法重写与多态&#xff08;第二部分&#xff09;六、抽象类与接口1、抽象类2、接口 七、类的关联与组合1、关联关系2、组合关系 八、面向对象设计原则1、SOLID原则2、设计…

实验 4:排序与查找

东莞理工的学生可以借鉴&#xff0c;请勿抄袭 1.实验目的 通过实验达到&#xff1a; 理解典型排序的基本思想&#xff0c;掌握典型排序方法的思想和相应实现算法&#xff1b; 理解和掌握用二叉排序树(BST)实现动态查找的基本思想和相应的实现 算法。 理解和掌握哈希(HASH)存…

【备战秋招】每日一题:2023.04.26-实习-第三题-MC方块

在线评测链接:P1231 题目内容 MC最新版本更新了一种特殊的方块&#xff0c;幽匿催发体。这种方块能够吸收生物死亡掉落的经验并感染周围方块&#xff0c;使其变成幽匿块。Steve想要以此为基础尝试搭建一个经验仓库&#xff0c;他来到了创造超平坦模式&#xff0c;在只有草方块…

[进阶]junit单元测试框架详解

单元测试 就是针对最小的功能单元(方法&#xff09;&#xff0c;编写测试代码对其进行正确性测试。 以前是如何进行单元测试的&#xff1f;有什么问题&#xff1f; 只能在main方法编写测试代码&#xff0c;去调用其他方法进行测试。无法实现自动化测试&#xff0c;一个方法测…

python数字猜谜2.0

改进了一下数字猜谜&#xff1a; 开头&#xff0c;可选等级&#xff1a; import random guess -1 c 0 print("数字猜谜游戏&#xff01;") n input("选择等级 A B C&#xff1a;") if (n "A") or (n "a"):guess random.randint…

模拟电路系列分享-阻容的频率响应

目录 概要 整体架构流程 技术名词解释 技术细节 1.以低通为例 2.高通电路&#xff1a; 总结&#xff1a; 概要 提示&#xff1a;这里可以添加技术概要 接着上一节的内容&#xff0c;这一节我们将介绍阻容的频率响应 整体架构流程 提示&#xff1a;这里可以添加技术整体架构…

模拟电路系列文章-放大电路输出电容

目录 概要 整体架构流程 技术名词解释 技术细节 小结 概要 提示&#xff1a;这里可以添加技术概要 一个运放组成的同相比例器&#xff08;包含运放内部结构&#xff09;所示&#xff0c;在它的输出端对地接了一个大电容C&#xff0c;这是一个极其危险的电路&#xff0c;一般会…

Java注解以及BigInteger类、BigDecimal类

说明&#xff1a; ① java.math包的BigInteger可以表示不可变的任意精度的整数。 ② 要求数字精度比较高&#xff0c;用到java.math.BigDecimal类 15是精度 后面大写的字母是要求要四舍五入 注解的理解 ① jdk 5.0 新增的功能 ② Annotation 其实就是代码里的特殊标记, 这些标…

C语言:使用指针打印数组内容

题目&#xff1a; 写一个函数打印arr数组的内容&#xff0c;不使用数组下标&#xff0c;使用指针。 arr是一维数组。 思路一&#xff1a;用 for循环 进行循环打印&#xff08;未写自定义函数&#xff09; 总体思路&#xff1a; &#xff08;一&#xff09;. 定义一维数组arr&a…

基于Hexo和Butterfly创建个人技术博客,(14) 给博客站点添加Aplayer音乐

Hexo官司网查看 这里 本章目标&#xff1a; 掌握aplayer音乐插件的用法给博客站点添加音乐功能 一、概述 个人比较倾向网站以简洁为主&#xff0c;并不赞成把网站做成花里虎哨的&#xff0c;比如添加鼠标特效或各种动态的元素。但个人站点的随意性比较大&#xff0c;虽没必要做…

【C++】内存管理、new和delete操作类型、operator new和operator delete函数、new和delete的实现原理

文章目录 1.C/C内存管理2.C语言的内存管理方式3.C内存管理方式3.1 new和delete操作内置类型3.2 new和delete操作自定义类型 4.operator new与operator delete函数5.new和delete的实现原理5.1内置类型5.2 自定义类型 1.C/C内存管理 在C/C中&#xff0c;内存管理是程序员负责管理…