【C语言:动态内存管理】

news2024/11/29 4:43:42

文章目录

  • 前言
  • 1.malloc
  • 2.free
  • 3.calloc
  • 4.realloc
  • 5.动态内存常见错误
  • 6.动态内存经典笔试题分析
  • 7.柔性数组
  • 8.C/C++中的内存区域划分

在这里插入图片描述

前言

文章的标题是动态内存管理,那什么是动态内存管理?为什么有动态内存管理呢?
回顾一下以前学的知识,我们已经掌握的开辟内存的方式有以下几种:

char ch = 'a';
int val = 10;
float f_val = 12.8f;
int arr[20] = { 0 };
//....

上述开辟内存的方式有几个弊端:

  1. 开辟空间的大小是固定的
  2. 数组在声明的时候,必须指定数组的长度,数组空间⼀旦确定了大小就不能调整了

但是对于空间的需求,不仅仅是上述的情况。有时候我们需要的空间大小在程序运行的时候才能知道,那数组在编译时就开辟空间的方式就不能满⾜了。
C语言引⼊了动态内存开辟,让程序员自己可以申请和释放空间,就比较灵活了。

1.malloc

在这里插入图片描述
这个函数的功能是:向内存申请一块连续的内存空间,然后返回一个指向该空间的起始地址的指针

  • 如果空间开辟成功,则返回一个指向该空间起始地址的指针。
  • 如果空间开辟失败,则返回一个NULL指针。因此,函数的返回值需要做检查
  • 开辟的空间不会被初始化
  • 返回值的类型是void*,所以该函数并不知道所开辟空间的类型,需要空间的使用者自己决定。
  • 当该函数的参数是0是,返回值是未定义的,取决于编译器。
  • 头文件stdlib.h

使用malloc函数申请40个字节的空间
在这里插入图片描述
当函数的参数是0时,vs2019中是随机值
在这里插入图片描述
既然空间是程序员申请的,那么最后由谁释放呢?

  1. 程序员自己释放。
  2. 程序结束,操作系统回收

下面就来介绍如何释放

2.free

C语言提供了另外⼀个函数free,专⻔是⽤来做动态内存的释放和回收的,函数原型如下:
在这里插入图片描述

  • 该函数用来释放由malloc、calloc、realloc申请的空间的
  • 如果参数 ptr 指向的空间不是动态开辟的,那free函数的行为是未定义的。
  • 如果参数 ptr 是NULL指针,则函数什么事都不做。
  • 该函数不会改变指针ptr的值,因此为了避免野指针的出现,要将其及时置为NULL。

在这里插入图片描述

3.calloc

在这里插入图片描述
该函数的功能跟malloc差不多,区别在于:

  • 该函数的参数有两个,一个是有几个元素,一个是每个元素的大小。
  • 该函数会将申请的空间初始化为全0。

在这里插入图片描述

4.realloc

在这里插入图片描述

realloc函数的出现让动态内存管理更加灵活。
有时会我们发现过去申请的空间太小了,有时候我们⼜会觉得申请的空间过⼤了,那为了合理的使用内存,我们⼀定会对内存的大小做灵活的调整。那 realloc 函数就可以做到对动态开辟内存大小的调整。

  • ptr 是要调整的内存地址
  • size 调整之后新大小
  • 返回值为调整之后的内存起始位置。
  • 这个函数调整原内存空间大小的基础上,还会将原来内存中的数据移动到新的空间
  • realloc在调整内存空间的是存在两种情况:
    • 情况1:原有空间之后有足够大的空间
    • 情况2:原有空间之后没有足够大的空间
  • 情况1
    当是情况1 的时候,要扩展内存就直接原有内存之后直接追加空间,原来空间的数据不发生变化
  • 情况2
    当是情况2的时候,原有空间之后没有⾜够多的空间时,扩展的⽅法是:在堆空间上另找⼀个合适⼤⼩连续空间来使⽤。这样函数返回的是⼀个新的内存地址。

既然该函数有两种特殊情况,那我们在使用时也应该更加注意:
下面的代码有没有问题呢?

int main()
{
	//1. 先申请一部分空间
	int* p = (int*)malloc(20);
	if (NULL == p)
	{
		perror("malloc");
		return 1;
	}
	//2.扩容空间
	p = (int*)realloc(p, 40);
	if (NULL == p)
	{
		perror("realloc");
		return 1;
	}
	//3.使用
	//.......
	
	free(p);
	p = NULL;
	return 0;
}

假如realloc开辟空间失败了,那么它会返回一个空指针,p就是空指针了。
好家伙,没给我扩容成功,我以前的数据也没了,真可以!
所以在接收realloc的返回值时,我们应该使用你一个临时变量接收,判断临时变量是不是空指针,然后再对p进行操作。
所以,应该这样写:

#include<stdlib.h>

int main()
{
	//1. 先申请一部分空间
	int* p = (int*)malloc(20);
	if (NULL == p)
	{
		perror("malloc");
		return 1;
	}
	//2.扩容空间
	int* tmp = NULL;
	tmp = (int*)realloc(p, 40);
	//先判断realloc的返回值
	if (tmp != NULL)
	{
		p = tmp;
		tmp = NULL;
	}
	//3.使用
	//....
	free(p);
	p = NULL;
	return 0;
}

5.动态内存常见错误

  1. 对NULL指针的解引用操作
  2. 对动态开辟空间的越界访问
  3. 对非动态开辟内存使用free释放

在这里插入图片描述

  1. 使用free释放⼀块动态开辟内存的⼀部分

在这里插入图片描述

  1. 对同⼀块动态内存多次释放

在这里插入图片描述

  1. 动态开辟内存忘记释放(内存泄漏)

6.动态内存经典笔试题分析

  1. 题目一
void GetMemory(char* p)
{
	p = (char*)malloc(100);
}
void Test(void)
{
	char* str = NULL;
	GetMemory(str);
	strcpy(str, "hello world");
	printf(str);
}

此处是值传递,并不会为str真正开辟空间,str还是NULL。
在这里插入图片描述

  1. 题目二
char* GetMemory(void)
{
	char p[] = "hello world";
	return p;
}
void Test(void)
{
	char* str = NULL;
	str = GetMemory();
	printf(str);
}

p变量生命周期为GetMemory函数内,函数结束,变量销毁。使用一个已经被销毁的变量的地址,结果是不可预测的
在这里插入图片描述

3.题目三

void Test(void)
{
	char* str = (char*)malloc(100);
	strcpy(str, "hello");
	free(str);
	if (str != NULL)
	{
		strcpy(str, "world");
		printf(str);
	}
}

str指向的空间被释放,那么str就是野指针了,但是str还是指向那块内存空间;向str中拷贝world,那就是非法访问了

7.柔性数组

  1. 什么是柔性数组

也许你从来没有听说过柔性数组(flexible array)这个概念,但是它确实是存在的。C99 中,结构中最后⼀个元素允许是未知大小的数组,这就叫做『柔性数组』成员。

struct s
{
	int i;
	char ch;
	int a[];  //int a[0]
};

其中a就是一个柔性数组。

  1. 柔性数组的特点
  • 结构中的柔性数组成员前⾯ 必须⾄少⼀个其他成员。
  • sizeof 返回的这种结构大小不包括柔性数组的内存
  • 包含柔性数组成员的结构体用malloc ()函数进行内存的动态分配,并且分配的内存应该大于结构体的大小,以适应柔性数组的预期大小。
  1. 柔性数组的使用

在这里插入图片描述

如果我们不使用柔性数组,那么如何实现上述代码呢?
在这里插入图片描述

虽然不适用柔性数组也能达到目的,但使用柔性数组有两个好处:

  1. 方便内存的释放

如果我们的代码是在⼀个给别⼈⽤的函数中,你在⾥⾯做了⼆次内存分配,并把整个结构体返回给⽤⼾。⽤⼾调⽤free可以释放结构体,但是⽤⼾并不知道这个结构体内的成员也需要free,所以你不能指望⽤⼾来发现这个事。所以,如果我们把结构体的内存以及其成员要的内存⼀次性分配好了,并返回给⽤⼾⼀个结构体指针,⽤⼾做⼀次free就可以把所有的内存也给释放掉。

  1. 有利于访问速度

连续的内存有益于提⾼访问速度,也有益于减少内存碎⽚。

8.C/C++中的内存区域划分

在这里插入图片描述

  1. 栈区

栈区(stack):在执⾏函数时,函数内局部变量的存储单元都可以在栈上创建,函数执⾏结束时这些存储单元⾃动被释放。栈内存分配运算内置于处理器的指令集中,效率很⾼,但是分配的内存容量有限。栈区主要存放运⾏函数⽽分配的局部变量、函数参数、返回数据、返回地址等

  1. 堆区

堆区(heap):⼀般由程序员分配释放,若程序员不释放,程序结束时可能由OS回收。分配⽅方式似于链表。

  1. 静态区

数据段(静态区):存放全局变量、static修饰的变量。程序结束后由系统释放。

  1. 代码段

代码段:存放函数体(类成员函数和全局函数)的⼆进制代码

本次的分享就到这里啦!如有错误请纠正。

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

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

相关文章

C++ Core Guidelines解析 ( 好书推荐 )

C Core Guidelines是Bjarne和 Herb Sutter发起编写的一个开源项目&#xff0c;汇聚了 C社区多年来积累的宝贵经验&#xff0c;是非常全面的编程最佳实践指导&#xff0c;包括代码风格、函数、类、错误处理、性能优化等&#xff0c;可以说是C社区的集大成者。用Effective Modern…

电脑主板支持的cpu型号汇总

一、如何选择不同的主板和对应CPU 1、看针脚&#xff1a;网上有相应的参数&#xff0c;只要CPU能安装到主板中&#xff0c;基本就兼容&#xff0c;这主要取决CPU插槽和主板插槽十分一致。 2、看型号&#xff1a;桌面处理器&#xff0c;只有Intel和AMD两大平台&#xff0c;他们对…

dToF直方图之美_deadtime死区时间

上节在激光雷达多目标测距中有个问题为什么激光雷达不用做pile up算法,有人会有疑问,我看过很多人的简历,都把pile up量产校正算法写为最为自豪重要的算法攻坚,可能会吸引一波人的眼球。这要是在两三年前是值得被肯定的,但是如今随着dToF非常多量产项目落地,pile up研究不…

【Hive】——安装部署

1 MetaData&#xff08;元数据&#xff09; 2 MetaStore &#xff08;元数据服务&#xff09; 3 MetaStore配置方式 3.1 内嵌模式 3.2 本地模式 3.3 远程模式 4 安装前准备 <!-- 整合hive --><property><name>hadoop.proxyuser.root.hosts</name><v…

BUUCTF-[GYCTF2020]FlaskApp flask爆破pin

这道题不需要爆破也可以getshell ssti都给你了 {{((lipsum.__globals__.__builtins__[__import__](so[::-1])[popen]("\x63\x61\x74\x20\x2f\x74\x68\x69\x73\x5f\x69\x73\x5f\x74\x68\x65\x5f\x66\x6c\x61\x67\x2e\x74\x78\x74")).read())}} 但是学习记录一下pin…

ETLCloud详解,如何实现最佳实践及问题排查

ETLCloud介绍 ETLCloud是新一代全域数据集成平台&#xff0c;领先于市场同类产品的数据集成平台(DataOps)&#xff0c;只需单击几下即可完成数据清洗转换、传输入仓等操作&#xff0c;具备高效、智能、一站式的全域数据集成优势&#xff0c;如&#xff1a; 毫秒级实时数据同步 …

MMLM之Gemini:《Introducing Gemini: our largest and most capable AI model》的翻译与解读

MMLM之Gemini&#xff1a;《Introducing Gemini: our largest and most capable AI model》的翻译与解读 导读&#xff1a;2023年12月6日&#xff0c;Google重磅发布大规模多模态模型Gemini&#xff0c;表示了Google语言模型发展到了一个新阶段&#xff0c;其多模态和通用能力明…

力扣1445 连续字符

目录 ​编辑 题目 示例 示例1 示例2 提示 详细解读 题目 给你一个字符串 s &#xff0c;字符串的「能量」定义为&#xff1a;只包含一种字符的最长非空子字符串的长度。 请你返回字符串 s 的 能量。 解题思路 这个问题的解法相对比较简单&#xff0c;可以通过遍历字…

int(1) 和 int(10) 的区别

int(1) 和 int(10) 的区别 最近遇到个问题&#xff0c;有个表的要加个user_id字段&#xff0c;user_id字段可能很大&#xff0c;于是我提mysql工单alter table xxx ADD user_id int(1)。领导看到我的sql工单&#xff0c;于是说&#xff1a;这int(1)怕是不够用吧&#xff0c;接…

Python finally-资源回收

有些时候&#xff0c;程序在 try 块里打开了一些物理资源&#xff08;例如数据库连接、网络连接和磁盘文件等&#xff09;&#xff0c;这些物理资源都必须被显式回收。 Python 的垃圾回收机制不会回收任何物理资源&#xff0c;只能回收内存中对象所占用的内存。 那么在哪里回收…

【智能家居】八、监控摄像采集、人脸识别比对进行开门功能点

一、使用 fswebcam 测试 USB 摄像头 二、根据demo来实现功能点 三、功能点编写编译运行实现 一、使用 fswebcam 测试 USB 摄像头 a. 安装 fswebcam orangepiorangepi:~$ sudo apt update orangepiorangepi:~$ sudo apt-get install -y fswebcamb. 安装完 fswebcam 后可以使用…

036.Python面向对象_self_cls_super

我 的 个 人 主 页&#xff1a;&#x1f449;&#x1f449; 失心疯的个人主页 &#x1f448;&#x1f448; 入 门 教 程 推 荐 &#xff1a;&#x1f449;&#x1f449; Python零基础入门教程合集 &#x1f448;&#x1f448; 虚 拟 环 境 搭 建 &#xff1a;&#x1f449;&…

AMC8美国数学竞赛历年真题集在线练习操作指南和2024年备考建议

今天是2023年12月10日&#xff0c;距离2024年的AMC8美国数学竞赛的举办还有40天时间。据六分成长了解&#xff0c;有一些孩子报名参加了AMC8的机构培训班系统学习&#xff0c;也有一些孩子选择了自己自学备考。 有家长问AMC8的培训是否一定要参加机构的培训班学习&#xff1f;…

Aho Corasick Algorithm

文章目录 前言介绍实现参考 前言 Aho Corasick Algorithm又叫AC自动机&#xff0c;该算法是一个匹配算法&#xff0c;用来匹配文本Text中多个patterns分别出现的次数&#xff1b; 我们定义n为patterns的总长度&#xff1b;m为Text的长度&#xff1b; 问题&#xff1a;在ahis…

C语言算法与数据结构,旅游景区地图求最短路径

背景&#xff1a; 本次作业要求完成一个编程项目。请虚构一张旅游景区地图&#xff0c;景区地图包括景点&#xff08;结点&#xff09;和道路&#xff08;边&#xff09;&#xff1a;地图上用字母标注出一些点&#xff0c;表示景点&#xff08;比如&#xff0c;以点 A、B、C、…

【C语言】字符串函数strcpystrcatstrcmpstrstr的使⽤和模拟实现

&#x1f308;write in front :&#x1f50d;个人主页 &#xff1a; 啊森要自信的主页 ✏️真正相信奇迹的家伙&#xff0c;本身和奇迹一样了不起啊&#xff01; 欢迎大家关注&#x1f50d;点赞&#x1f44d;收藏⭐️留言&#x1f4dd;>希望看完我的文章对你有小小的帮助&am…

聚类算法的性能度量

聚类算法的性能度量 聚类算法就是根据数据中样本与样本之间的距离或相似度&#xff0c;将样本划分为若干组&#xff0f;类&#xff0f;簇&#xff0c;其划分的原则&#xff1a;簇内样本相似、簇间样本不相似&#xff0c;聚类的结果是产生一个簇的集合。 其划分方式主要分为两…

前后端联调神器《OpenAPI-Codegen》

在后端开发完接口之后&#xff0c;前端如果再去写一遍接口来联调的话&#xff0c;会很浪费时间&#xff0c;这个时候使用OpenAPI接口文档来生成Axios接口代码的话&#xff0c;会大大提高我们的开发效率。 Axios引入 Axios是一个基于Promise的HTTP客户端&#xff0c;用于浏览器…

leetcode每日一题39

122.买卖股票的最佳时机II 确定dp数组&#xff08;dp table&#xff09;以及下标的含义 dp[i][j] j0 不持有股票 j1持有股票 i&#xff1a;第i天 dp[i][j]&#xff1a;第i天&#xff0c;持有状态为j时的最大现金确定递推公式 dp[i][0]max(dp[i-1][0], dp[i-1][1]prices[i]); 第…

Linux权限(用户角色+文件权限属性)

Linux权限 文章目录 Linux权限一.文件权限1.快速掌握修改权限的方法&#xff08;修改文件权限属性&#xff09;2.对比权限的有无&#xff0c;以及具体的体现3.修改权限的第二套方法&#xff08;修改用户角色&#xff09;4.文件类型&#xff08;Linux下一切皆文件&#xff09; 二…