动态内存管理(上)

news2024/11/25 20:53:37

目录

  • 为什么要有动态内存分配
  • malloc和free
    • malloc
    • free
  • calloc和realloc
  • calloc
    • realloc

感谢各位大佬对我的支持,如果我的文章对你有用,欢迎点击以下链接
🐒🐒🐒 个人主页
🥸🥸🥸 C语言
🐿️🐿️🐿️ C语言例题
🐣🐓🏀 python

为什么要有动态内存分配

在学动态内存分布时我们先了解一下内存的一点知识
在这里插入图片描述
内存是分栈区,堆区和静态区

我们已经掌握的内存开辟⽅式有
int val = 20;//在栈空间上开辟四个字节
char arr[10] = {0};//在栈空间上开辟10个字节的连续空间

但是上述的开辟空间的方式有两个特点:
空间开辟大小是固定的
数组在申明的时候,必须指定数组的长度,数组空间一旦确定了大小不能调整

但是对于空间的需求,不仅仅是上述的情况。
有时候我们需要的空间大小在程序运行的时候才能知道,那数组的编译时开辟空间的方式就不能满足了。

而如果我们一次性就开辟很大的空间,比如我只明明只需要开辟4个字节的空间,而最后却开辟了100个字节的空间,那么多出来的空间就浪费掉了

因此C语言引人了动态内存开辟,让程序员自己可以申请和释放空间,就比较灵活了。(注意必须最后要释放,如果只申请不释放的话内存会越来越大)

malloc和free

malloc

C语言提供了一个动态内存开辟的函数malloc:

void* malloc (size_t size);

这个函数向内存申请一块连续可用的空间,并返回指向这块空间起始地址的指针
如果开辟成功,则返回一个指向开辟好空间的指针
如果开辟失败,则返回一个 NULL 指针,因此malloc的返回值一定要做检查(用perror(“malloc”)来检查)
返回值的类型是 void ,所以malloc函数并不知道开辟空间的类型,具体在使用的时候使用者自己来决定*。
如果参数 size 为0,malloc的行为是标准是未定义的,取决于编译器
什么情况会使内存开辟失败呢?
来看看下面的一个代码

#include<stdio.h>
#include<limits.h>
#include<stdlib.h>
int main()
{
	int* p = (int*)malloc(INT_MAX*10);
	if (p == NULL)
	{
	perror("malloc");
		return 1;//非0表示异常返回
	}
	return 0;//0表示正常返回
}

INT_MAX是整形的最大值,他的值如图
在这里插入图片描述
在我们还没有运行的时候系统就已经报错了
在这里插入图片描述
我们再看看perror输出的结果

在这里插入图片描述
显然我们开辟的空间过大,导致没有了足够的空间

如果我们申请0个字节会怎么样
在这里插入图片描述
并没有报错
那我们再申请-1个字节又会怎么样
在这里插入图片描述
我们可以看到输出的结果是没有足够的空间

我们再看一个代码

#include<stdio.h>
#include<limits.h>
#include<stdlib.h>
int main()
{
	int* p = (int*)malloc(10*sizeof(int));
	if (p == NULL)
	{
		perror("malloc");
		return 1;
	}
	for (int i = 0; i < 10; i++)
	{
		*(p + i) = i;
	}
	for (int i = 0; i < 10; i++)
	{
		printf("%d ", p[i]);
	}
	return 0;

}

在这里插入图片描述
这里的p指向malloc开辟的起始地址,由于内存中存放的都是int类型的元素,那么p+i就表示p跳过i个元素,对其解引用就是内存中的具体值,这里就非常像数组
在这里插入图片描述

free

C语言提供了另外一个函数free,专门是用来做动态内存的释放和回收的,函数原型如下

void free (void* ptr);

free函数用来释放动态开辟的内存
如果参数 ptr 指向的空间不是动态开辟的,那free函数的行为是未定义的
如果参数 ptr 是NULL指针,则函数什么事都不做
malloc和free都声明在 stdlib.h 头文件中
在之前有提到,每次都要释放内存,而在malloc的代码中却没有释放,这里需要解释一下

#include<stdio.h>
#include<limits.h>
#include<stdlib.h>
int main()
{
	int* p = (int*)malloc(10*sizeof(int));
	if (p == NULL)
	{
		perror("malloc");
		return 1;
	}
	for (int i = 0; i < 10; i++)
	{
		*(p + i) = i;
	}
	for (int i = 0; i < 10; i++)
	{
		printf("%d ", p[i]);
	}
	return 0;

}

由于这个程序是运行完了的,所以系统会自动回收内存,因此并不需要我们主动去回收(当然了主动回收也是一个好习惯),而有些程序是一直运行的,或者会运行很久,这样导致开辟的空间一直没有释放,而在运行的过程中不断的开辟新的内存空间,导致内存越来越大,所以需要自己去主动释放内存

最后结论就是如果程序运行完,就会自己回收内存空间,这是被动回收
而用free回收的话,就是可以在程序还没有运行完就可以回收,这是主动回收

举个例子:

#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 = NULL;
	return 0;
}

在这里插入图片描述
这里free时我们只是回收了arr的内存空间,但是ptr却仍然保存着arr的地址,因此在最后我们需要让ptr==NULL变成空指针,否则就会变成野指针

calloc和realloc

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));//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初始化,使空间中的元素全为0,所以输出的结果都是0

#include <stdio.h>
#include <stdlib.h>
int main()
{
	int* p = (int*)malloc(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;
}

在这里插入图片描述
malloc没有初始话,导致输出的结果就是这样

所以如果我们对申请的内存空间的内容要求初始化,那么可以很方便的使用calloc函数来完成任务

realloc

realloc函数的出现让动态内存管理更加灵活。

有时会我们发现过去申请的空间太小了,有时候我们又会觉得申请的空间过大了,那为了合理的时候内存,我们⼀定会对内存的大小做灵活的调整

那 realloc 函数就可以做到对动态开辟内存大小的调整

函数原型如下

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

ptr 是要调整的内存地址
size 调整之后新大小
返回值为调整之后的内存起始位置
这个函数调整原内存空间大小的基础上,还会将原来内存中的数据移动到新的空间
realloc在调整内存空间的是存在两种情况
◦ 情况1:原有空间之后有足够大的空间
◦ 情况2:原有空间之后没有足够大的空间

情况1
当是情况1的时候,要扩展内存就直接原有内存之后直接追加空间,原来空间的数据不发生变化
在这里插入图片描述

情况2
当是情况2的时候,原有空间之后没有足够多的空间时,扩展的方法是:在堆空间上另找⼀个合适大小
的连续空间来使用。这样函数返回的是一个新的内存地址

在这里插入图片描述
我们具体举个例子

#include <stdio.h>
#include <stdlib.h>
int main()
{
	int* ptr = (int*)malloc(100 * sizeof(int));
	//扩展容量 
	int* p = NULL;
	p = realloc(ptr, 10000 * sizeof(int));
		ptr = p;
	free(ptr);
	return 0;
}

在这里插入图片描述
在这里插入图片描述
显然这里的realloc是重新找了一块新的内存空间,并返回的

因此由于上述的两种情况,realloc函数的使用就要注意⼀些。

//代码1 
#include <stdio.h>
#include <stdlib.h>
int main()
{
	int* ptr = (int*)malloc(100);
	if (ptr != NULL)
	{
		//业务处理
	} 
	else
	{
	return 1;
	} 
	//扩展容量
		ptr = (int*)realloc(ptr, 1000);//这样可以吗?(如果申请失败会如何?)	
		free(ptr);
	return 0;
}
//代码2
#include <stdio.h>
#include <stdlib.h>
int main()
{
	int* ptr = (int*)malloc(100*sizeof(int));
	if (ptr != NULL)
	{
		//业务处理
	} 
	else
	{
	return 1;
	} 
	//扩展容量 
	int* p = NULL;
	p = realloc(ptr, 1000*sizeof(int));
	if (p != NULL)
	{
		ptr = p;
	} 
		free(ptr);
	return 0;
}

代码一和代码二的区别就在于一个是直接用ptr接收realloc的返回值
一个是判断realloc的返回值是否为空
为什么需要判断返回值是否为空呢?

如果返回值为空,那么让ptr等于空指针的话,之前内存所保存的所有元素都被消失(也可以说是之前申请的内存都没有了,当然就没有保存的元素了)

而如果判断返回值不为空之后,再将返回值传给ptr,那么内存保存的所以元素都不会消失

此外realloc还可以当成malloc使用

#include<stdio.h>
int main()
{
	realloc(NULL,sizeof(int))//==malloc(1*sizeof(int))
	return 0;
}

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

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

相关文章

gitblit 搭建本地服务器

gitblit 搭建本地的 git 服务器 一、简介 Gitblit是一个用于管理,查看和提供Git存储库的开源纯Java堆栈。它主要设计为希望托管集中式存储库的小型工作组的工具。 Gitblit 是一个和 Gitlab 、github、gitee 功能差不多的 git 远程仓库系统,Gitblit 开源免费、兼容性强、支持…

C++:this指针和构造与析构的运用

目录 一&#xff0c;this指针 二&#xff0c;构造函数 三&#xff0c;析构函数 四&#xff0c;析构与构造的调用 一&#xff0c;this指针 首先&#xff0c;我们先观察以下类&#xff1a; #include <iostream> using namespace std; class Date { public: void In…

二十、W5100S/W5500+RP2040树莓派Pico<MQTT连接阿里云控制板载LED>

1. 前言 物联网平台提供安全可靠的设备连接通信能力&#xff0c;支持设备数据采集上云&#xff0c;规则引擎流转数据和云端数据下发设备端。此外&#xff0c;也提供方便快捷的设备管理能力&#xff0c;支持物模型定义&#xff0c;数据结构化存储&#xff0c;和远程调试、监控、…

mindspore mindcv图像分类算法;模型保存与加载

参考&#xff1a; https://www.mindspore.cn/tutorials/en/r1.3/save_load_model.html https://github.com/mindspore-lab/mindcv/blob/main/docs/zh/tutorials/finetune.md 1、mindspore mindcv图像分类算法 import os from mindcv.utils.download import DownLoad import o…

举个栗子!Tableau 技巧(259):文本表中省市县数据的灵活逐级下钻「方法一」

之前&#xff0c;我们分享过 &#x1f330;&#xff1a;实现地图中的省市县逐级下钻。有数据粉提出问题&#xff1a;如果不是地图&#xff0c;而是文本表&#xff0c;有什么办法可以像这样&#xff0c;实现地理位置逐级下钻呢&#xff1f; 文本表也是可以的。但是&#xff0c;…

Count-based exploration with neural density models论文笔记

Count-based exploration with neural density models[J]. International Conference on Machine Learning,International Conference on Machine Learning, 2017. 基于计数的神经密度模型探索 0、问题 这篇文章的关键在于弄懂pseudo-count的概念&#xff0c;以及是如何运用…

【Leetcode】202. 两数之和

给定一个整数数组 nums 和一个整数目标值 target&#xff0c;请你在该数组中找出 和为目标值 target 的那 两个 整数&#xff0c;并返回它们的数组下标。 你可以假设每种输入只会对应一个答案。但是&#xff0c;数组中同一个元素在答案里不能重复出现。 你可以按任意顺序返回…

Java 身份证号校验,根据身份证号识别出生地

Java 身份证号校验&#xff1a; import org.apache.commons.lang.StringUtils;import java.util.Calendar; import java.util.Collections; import java.util.HashMap; import java.util.Map;/*** desc 身份证工具类* auth llp* date 2022/7/7 16:13*/ public class IdCardNum…

Java算法(三): 判断两个数组是否为相等 → (要求:长度、顺序、元素)相等

Java算法&#xff08;三&#xff09; 需求&#xff1a; 1. 定义一个方法&#xff0c;用于比较两个数组是否相同2. 需求&#xff1a;长度&#xff0c;内容&#xff0c;顺序完全相同package com.liujintao.compare;public class SameArray {public static void main (String[] a…

JAVA微信端医院3D智能导诊系统源码

医院智能导诊系统利用高科技的信息化手段&#xff0c;优化就医流程。让广大患者有序、轻松就医&#xff0c;提升医疗服务水平。 随着人工智能技术的快速发展&#xff0c;语音识别与自然语言理解技术的成熟应用&#xff0c;基于人工智能的智能导诊导医逐渐出现在患者的生活视角中…

小红书达人投放比例是多少合适?品牌方必看

品牌做小红书种草推广想要产生更好的效果&#xff0c;是需要素人和达人按照一定比例去进行投放的&#xff0c;素人铺量可以让产品产生迅速曝光的效果&#xff0c;少量达人投放可以让产品产生更好的转化效果。 小红书达人投放具有较高的互动性和口碑传播效果。达人通过自身的影…

打开pr提示找不到vcomp100.dll无法继续执行代码怎么办?5种dll问题解决方案全解析

vcomp100.dll是一个由Microsoft开发的动态链接库&#xff08;DLL&#xff09;文件&#xff0c;它对于许多基于图形的应用程序&#xff08;如Photoshop&#xff09;和多个游戏&#xff08;如《巫师3》&#xff09;至关重要。以下是关于vcomp100.dll的属性介绍以及找不到vcomp100…

小程序如何部署SSL证书

微信小程序开发前提必须拥有一本SSL证书&#xff0c;办理SSL证书之前确保好指定的微信小程序开发接口使用的域名&#xff0c;如果没有域名的提前申请好&#xff0c;并且到国内服务器提供商去办理备案。 了解微信小程序使用SSL证书的作用&#xff0c;包括以下三个方面&#xff1…

[C语言基础]文件读取模式简析

文件操作 打开方式介绍r / rb模式w / wb模式 打开方式介绍 函数fopen可打开一个文件&#xff0c;返回值是文件指针FILE * 第一个参数是文件路径&#xff0c;第二个参数是打开方式mode 参数可为以下几种&#xff1a; r/w/a/r/w/a/rb/wb/ab/rb/wb/ab 其中&#xff0c; r 为只读&…

求臻医学MRD产品喜获北京市新技术新产品(服务)证书

近日&#xff0c;北京市科学技术委员会、中关村科技园区管理委员会、北京市发展和改革委员会等五大部门联合公示了2023年度第一批&#xff08;总第十八批&#xff09;北京市新技术新产品&#xff08;服务&#xff09;名单。凭借领先的技术能力、产品创新能力及质量可靠性等优势…

大数据毕业设计选题推荐-河长制大数据监测平台-Hadoop-Spark-Hive

✨作者主页&#xff1a;IT研究室✨ 个人简介&#xff1a;曾从事计算机专业培训教学&#xff0c;擅长Java、Python、微信小程序、Golang、安卓Android等项目实战。接项目定制开发、代码讲解、答辩教学、文档编写、降重等。 ☑文末获取源码☑ 精彩专栏推荐⬇⬇⬇ Java项目 Python…

VM虚拟机安装

想编译一个 c 代码 windows 转成 linux 安装一个vm 准备一个虚拟机安装包&#xff0c;双击&#xff0c;开始安装 下一步 缸盖安装位置路径&#xff0c;添加PATH&#xff0c;下一步 下一步 添加到桌面&#xff0c;加入开始菜单&#xff0c;下一步 打开桌面的软件图标&#…

Panorama SCADA平台的警报通知功能配置详解

1. 前言 SCADA系统的主要目标是采集与监控工业过程数据&#xff0c;以确保工业生产正常运行。通过实时警报通知功能&#xff0c;操作人员可以立即获取有关潜在问题的信息&#xff0c;从而能够快速采取行动解决问题&#xff0c;防止进一步的损害或生产中断。因此&#xff0c;及…

小程序版本审核未通过,需在开发者后台「版本管理—提交审核——小程序订单中心path」设置订单中心页path,请设置后再提交代码审核

小程序版本审核未通过&#xff0c;需在开发者后台「版本管理—提交审核——小程序订单中心path」设置订单中心页path&#xff0c;请设置后再提交代码审核 因小程序尚未发布&#xff0c;订单中心不能正常打开查看&#xff0c;请先发布小程序后再提交订单中心PATH申请 初次提交…

03【远程协作开发、TortoiseGit、IDEA绑定Git插件的使用】

上一篇&#xff1a;02【Git分支的使用、Git回退、还原】 下一篇&#xff1a;【已完结】 目录&#xff1a;【Git系列教程-目录大纲】 文章目录 一、远程协作开发1.1 远程仓库简介1.1.1 Github1.1.2 Gitee1.1.3 其他托管平台 1.2 发布远程仓库1.2.1 创建项目1&#xff09; 新…