C语言第三十三弹---动态内存管理(上)

news2024/9/21 10:56:15

个人主页: 熬夜学编程的小林

💗系列专栏: 【C语言详解】 【数据结构详解】

动态内存管理

1、为什么要有动态内存分配

2、malloc和free

2.1、malloc

2.2、free

3、calloc和realloc

3.1、calloc

3.2、realloc

4、常见的动态内存的错误

总结


1、为什么要有动态内存分配

我们已经掌握的内存开辟方式有:
int val = 20;//在栈空间上开辟四个字节
char arr[10] = {0};//在栈空间上开辟10个字节的连续空间
但是上述的开辟空间的方式有两个特点:
空间开辟大小是固定的。
数组在申明的时候,必须指定数组的长度,数组空间⼀旦确定了大小不能调整
但是对于空间的需求,不仅仅是上述的情况。有时候我们需要的空间大小在程序运行的时候才能知
道,那数组的编译时开辟空间的方式就不能满足了。
C语言引入了动态内存开辟,让程序员自己可以申请和释放空间,就比较灵活了。

2、malloc和free

2.1、malloc

C语言提供了⼀个动态内存开辟的函数:
void* malloc (size_t size);//size为字节数
这个函数向内存申请一块连续可用的空间,并返回指向这块空间的指针。
如果开辟成功,则返回⼀个指向开辟好空间的指针。
如果开辟失败,则返回⼀个 NULL 指针,因此malloc的返回值⼀定要做检查。
返回值的类型是 void* ,所以malloc函数并不知道开辟空间的类型,具体在使用的时候使用者自己来决定。
如果参数 size 为0,malloc的行为是标准是未定义的,取决于编译器。

2.2、free

C语言提供了另外⼀个函数free,专门是用来做动态内存的释放和回收的,函数原型如下:
void free (void* ptr);
free函数用来释放动态开辟的内存。
如果参数 ptr 指向的空间不是动态开辟的,那free函数的行为是未定义的。
如果参数 ptr 是NULL指针,则函数什么事都不做。
malloc和free都声明在 stdlib.h 头文件中。
举个例子:
#include <stdio.h>
#include <stdlib.h>
int main()
{
 int num = 0;
 scanf("%d", &num);
 int arr[num] = {0};
 int* ptr = NULL;
 ptr = (int*)malloc(num*sizeof(int));
 if(NULL != ptr)//判断ptr指针是否为空 不为空则赋值
 {
 int i = 0;
 for(i=0; i<num; i++)
 {
 *(ptr+i) = 0;
 }
 }
 free(ptr);//释放ptr所指向的动态内存
 ptr = NULL;//是否有必要? 建议手动置空,防止野指针问题
 return 0;
}

指针释放之后是否要手动置空,博主的建议是最好手动置空,防止出现野指针。

3、calloc和realloc

3.1、calloc

C语言还提供了⼀个函数叫 calloc calloc 函数也用来动态内存分配。原型如下:
void* calloc (size_t num, size_t size);
函数的功能是为 num 个大小为 size 的元素开辟⼀块空间,并且把空间的每个字节初始化为0。
与函数 malloc 的区别只在于 calloc 会在返回地址之前把申请的空间的每个字节初始化为全0。
举个例子:
#include <stdio.h>
#include <stdlib.h>
int main()
{
 int *p = (int*)calloc(10, sizeof(int));
 if(NULL != p)
 {
 int i = 0;
 for(i=0; i<10; i++)
 {
 printf("%d ", *(p+i));
 }
 }
 free(p);
 p = NULL;
 return 0;
}
输出结果:
所以如果我们对申请的内存空间的内容要求初始化,那么可以很方便的使用calloc函数来完成任务。

3.2、realloc

realloc函数的出现让动态内存管理更加灵活。
有时会我们发现过去申请的空间太小了,有时候我们又会觉得申请的空间过大了,那为了合理的时候内存,我们⼀定会对内存的大小做灵活的调整。那 realloc 函数就可以做到对动态开辟内存大小的调整。
函数原型如下:
void* realloc (void* ptr, size_t size);
ptr 是要调整的内存地址
size 调整之后新大小(字节数)
返回值为调整之后的内存起始位置。
这个函数调整原内存空间大小的基础上,还会将原来内存中的数据移动到新的空间。
realloc在调整内存空间的是存在两种情况:
情况1:原有空间之后有足够大的空间
情况2:原有空间之后没有足够大的空间
情况1
当是情况1 的时候,要扩展内存就直接原有内存之后直接追加空间,原来空间的数据不发生变化。
情况2
当是情况2 的时候,原有空间之后没有足够多的空间时,扩展的方法是:在堆空间上另找⼀个合适大小的连续空间来使用。这样函数返回的是⼀个新的内存地址。
由于上述的两种情况,realloc函数的使用就要注意⼀些
#include <stdio.h>
#include <stdlib.h>
int main()
{
	int* ptr = (int*)malloc(100);
	if (ptr != NULL)
	{
		//业务处理
	}
	else
	{
		return 1;
	}
	//扩展容量

	//代码1 - 直接将realloc的返回值放到ptr中
	ptr = (int*)realloc(ptr, 1000);//这样可以吗?(如果申请失败会如何?)
	// realloc可能申请空间失败,因此不推荐直接赋值给ptr,
	// 而是创建一个临时指针变量,如果确定申请成功再将临时指针变量赋值给ptr,即代码2方式

	//代码2 - 先将realloc函数的返回值放在p中,不为NULL,在放ptr中
	int* p = NULL;
	p = realloc(ptr, 1000);
	if (p != NULL)
	{
		ptr = p;// 推荐使用该方式,防止空间申请失败情况
	}
	//业务处理
	free(ptr);
	return 0;
}

总结:动态开辟的空间建议不要直接赋值给想要处理数据的指针变量,而是先判断再进行赋值。

4、常见的动态内存的错误

1、对NULL指针的解引用操作

void test()
 {
 int *p = (int *)malloc(INT_MAX/4);
 *p = 20;//如果p的值是NULL,就会有问题
 free(p);
 }

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);
 }

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

void test()
 {
 int a = 10;
 int *p = &a;
 free(p);//ok? free只能释放动态开辟的内存空间
 }

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

void test()
 {
 int *p = (int *)malloc(100);
 p++;
 free(p);//p不再指向动态内存的起始位置
 }

5、对同一块动态内存多次释放

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

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

void test()
 {
 int *p = (int *)malloc(100);
 if(NULL != p)
 {
 *p = 20;
 }
 }
int main()
 {
 test();
 while(1);
 }
忘记释放不再使用的动态开辟的空间会造成内存泄漏。
切记:动态开辟的空间⼀定要释放,并且正确释放。

总结


本篇博客就结束啦,谢谢大家的观看,如果公主少年们有好的建议可以留言喔,谢谢大家啦!

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

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

相关文章

【单片机学习的准备】

文章目录 前言一、找一个视频是二、画图软件三、装keil5 仿真protues总结 前言 提示&#xff1a;这里可以添加本文要记录的大概内容&#xff1a; 项目需要&#xff1a; 提示&#xff1a;以下是本篇文章正文内容&#xff0c;下面案例可供参考 一、找一个视频是 https://www.b…

mariadb数据库——安装,创建数据库

MariaDB是一个流行的开源关系型数据库管理系统&#xff08;RDBMS&#xff09;&#xff0c;它是MySQL的一个分支。 安装 apt -y install mariadb-servervi /etc/mysql/mariadb.conf.d/50-server.cnf character-set-server utf8mb4 collation-server utf8mb4_general_c…

让AI给你写代码,初体验(三)- AI加上格式化对话,实现对单个文件的修改/保存

上一篇中&#xff0c;AI生成web应用初步解决了简单web应用生成&#xff0c;但是存在两方面的问题 1&#xff09; 无法向AI提供增量需求&#xff0c;特别是一个对话结束之后&#xff0c;只能把全部需求&#xff08;包括新需求&#xff09;再描述一遍 2&#xff09; 无法用AI对存…

深入学习NumPy库在数据分析中的应用场景

在数据科学与机器学习领域&#xff0c;NumPy&#xff08;Numerical Python&#xff09;是一个经常被提及的重要工具。它是Python语言中一个非常强大的库&#xff0c;提供了高性能的多维数组对象以及用于处理这些数组的工具。NumPy不仅仅是一个用于数值计算的库&#xff0c;它还…

Redis第6讲——主从复制模式详解

Redis的读写性能很高&#xff0c;但在面对大规模数据和高发访问的挑战时&#xff0c;单节点的Redis可能无法满足需求&#xff0c;这就引出了Redis集群的概念。本节先介绍一下Redis高可用方案之一的主从复制模式&#xff0c;虽说现在基本不会用这种模式&#xff0c;但是无论是哨…

Web自动化测试平台开发---Automated_platform

一、项目简介 历时一个假期&#xff0c;Automated_platform 第一版完工&#xff0c;是一款基于po模式的自动化测试平台,采用后端技术为DjangoceleryRabbitMQmysql 配置mysql数据库&#xff0c;进行数据迁移后&#xff0c;运行项目后&#xff0c;即可成功访问http://127.0.0.1:8…

单调栈的理解

单调栈的理解 核心代码场景思考 完整代码 单调栈&#xff1a; 单调递增或 单调递减的栈 核心代码 while (!s.empty()&&s.peek()<nums[i]){s.pop(); } s.push(nums[i]);将要放入的元素&#xff0c;与栈内元素依个比较&#xff0c;小于的都出栈&#xff0c;最后将要…

List 集合遍历过程中删除元素避坑指南。

文章目录 1. 遍历2. 遍历过程中删除元素2.1 for 简单循环正向遍历方式2.2 for 简单循环反向遍历方式2.3 foreach 方式遍历删除2.4 Iterator的remove()方法2.5 <font color green> removeIf() &#xff08;推荐&#xff09;<green>2.6 Strem 方式 作为一名后端开发…

Executable and Linkable Format(ELF)

File layout ELF文件有两种视图。程序头表&#xff08;Program Header&#xff09;显示在运行时使用的段&#xff08;Segments&#xff09;&#xff0c;而节头表&#xff08;Section Header&#xff09;则列出了二进制文件的所有节&#xff08;Sections&#xff09;的集合。程…

SCT2633STER:4.5V-60V Vin,3A,高效降压DCDC转换器

•宽输入范围&#xff1a;4.5V-60V •高达3A的连续输出电流 •1.221V1%反馈参考电压 •集成220mΩ高压侧MOSFET •低静态电流为300uA •轻负载下的脉冲跳过模式&#xff08;PSM&#xff09; •最小接通时间100ns •内置12ms软启动时间 •简易内部补偿 •固定频率500KH…

使用Docker搭建一款实用的个人IT工具箱——It-Tools

作为程序员&#xff0c;在日常工作中&#xff0c;需要借助一些工具来提高我们工作效率&#xff0c;IT-Tools是为开发人员度身打造的一套便捷在线工具。它提供全面功能&#xff0c;使开发者能以更高效方式完成任务。经由IT-Tools&#xff0c;开发人员能轻松应对各类技术挑战&…

怎么抠图把把人物扣下来?简单快捷的抠图方法

相信很多新手小白在初入设计行业时&#xff0c;对于抠图怎么把人物扣下来都是一头雾水。抠图作为设计中常用的一种技术&#xff0c;能够帮助我们快速提取图片中的某个部分&#xff0c;进行合成或者修改。对于老手来说&#xff0c;抠图或许是再熟悉不过的操作&#xff0c;但对于…

WPF中如何设置自定义控件

1.圆角按钮的设置&#xff1a; 众所周知在WPF中自带有提示信息&#xff0c;当我问创建Button时&#xff0c;点击空格出现如下可选设置 带有小扳手&#x1f527;图标为相应的属性&#xff0c;如果Button有CornerRadius&#xff08;角半径&#xff09;属性就能够直接设置Button实…

Unity中URP实现水体(整理优化)

文章目录 前言一、优化水的深度1、我们把 水流动的方向 和 水深浅过渡值&#xff0c;整合到一个四维变量中2、修改 水体流动方向3、在片元着色器中&#xff0c;修改使用过渡变量 二、优化泡沫三、优化水下的扭曲1、修复原本扰动UV的计算 四、优化水面高光1、把高光强度、光滑度…

如何用ai智能写作?推荐5款优秀的AI写作神器

写作是件繁琐的事情&#xff0c;也是需要耗费精力和时间的&#xff0c;还好目前有了ai写作神器的出现&#xff0c;帮助我们解决了不少的写作难题。这些AI写作工具不仅可以帮助我们提高写作效率&#xff0c;还能够生成高质量的内容。在本文中&#xff0c;我将向你推荐5款优秀的A…

MATLAB中strel函数用法

目录 语法 说明 任意邻域形状 二维几何邻域形状 三维几何邻域形状 示例 创建正方形结构元素 创建线形结构元素 创建盘形结构元素 创建三维球形结构元素 参数说明 属性 strel函数的功能是形态学结构元素。 语法 SE strel(nhood) SE strel("diamond",r…

BUUCTF AWD-Test1

打开靶场是这个有些简陋的界面。 随便点点&#xff0c;找到这个东西。 看到ThinkPHP&#xff0c;思路瞬间清晰&#xff0c;老熟人了。这个就是ThinkPHP漏洞。根据版本我们去找一下poc。 /index.php/?sIndex/\think\View/display&content%22%3C?%3E%3C?php%20phpinfo();…

Git工具Clone项目报错:OpenSSL SSL_read: SSL_ERROR_SYSCALL, errno 10054

目录 问题现象&#xff1a; 解决方法&#xff1a; 问题现象&#xff1a; 今天在项目中使用git工具clone远程库的项目地址时&#xff0c;遇到了如下报错&#xff0c;导致无法克隆项目代码到本地&#xff1a; OpenSSL SSL_read: SSL_ERROR_SYSCALL, errno 10054 解决方法&#…

【从Python基础到深度学习】9.Python 语法基础

一、常量与变量 常量:程序中使用的具体的数、字符。在运行过程中&#xff0c;值无法更改 变量:表示一一个存储单元&#xff0c;其中存储的值可以修改 如&#xff1a;a5,b6 变量命名: 1、只能包含字母、数字、下划线 2、只能以字母、下划线开头 3、不要使用关键字作为变量名称 …

浅谈mysql mvcc

目录 前言 mvcc 是如何工作的&#xff1f; 数据的更新 前言 mvcc 与一个事物的隔离级别有关&#xff0c;未提交读永远读的是当前值&#xff0c;串行化是通过加锁实现&#xff0c;这两种隔离级别都与mvcc 没有任何关系。只要一提到mvcc应该想到的是读提交以及可重复读&#…