C语言中灵活多变的动态内存,malloc函数 free函数 calloc函数 realloc函数

news2024/9/23 7:23:07

请添加图片描述

文章目录

  • 🚀前言
  • 🚀管理动态内存的函数
    • ✈️malloc函数
    • ✈️free函数
    • ✈️calloc函数
    • ✈️realloc函数
  • 🚀在使用动态内存函数时的常见错误
    • ✈️对NULL指针的解引用
    • ✈️ 对动态开辟空间的越界访问
    • ✈️对非动态开辟内存使用free释放
    • ✈️使用free释放一块动态开辟内存的一部分
    • ✈️对同一块动态内存多次释放
    • ✈️动态开辟内存忘记释放(内存泄漏)

🚀前言

铁子们好啊!今天阿辉给大家讲一下C语言里面特别重要的一块知识——动态内存管理,为什么说它重要呢?相信大家在使用数组时都遇到过数字开辟空间太大或者太小的情况,这是很尴尬的因为数组空间一旦确定就难以扩大或者缩小。而今天的动态内存管理将帮我们很好的解决这一窘境。话不多说,开始咱们今天的学习吧!!!

🚀管理动态内存的函数

动态内存的管理其实也就分为两块

  • 空间的开辟和扩容
  • 空间的释放

而要做到以上两点,我们要引入几个重要的管理动态内存的函数

动态内存的开辟和释放都是在堆区上

✈️malloc函数

malloc函数是用来开辟空间的,如何使用呢?下面是它的声明👇

void* malloc(size_t size);

关于malloc函数的注意事项:

  • size表示malloc函数所开辟空间的大小,单位是字节
  • maloc函数对于开辟空间存放何种类型的数据并不关心,返回值是void*类型,malloc开辟空间成功,则返回这块空间的起始位置的地址;若开辟空间失败,则返回空指针NULL
  • malloc传入的参数为0时,这种行为是C语言标准未定义的,取决于编译器
  • malloc函数开辟的空间,里面的内容是随机的

具体使用:

int mian()
{
	int* p = (int*)malloc(sizeof(int) * 10);//开辟10*4个字节的空间,返回值强转为int*
	if (p == NULL)//判断动态内存是否开辟成功
	{
		printf("malloc 开辟空间失败\n");//开辟失败返回调试语句,就是让程序员知道哪错了
		return 1;//开辟失败直接返回
	}
	//对动态开辟内存的进一步使用
	//这玩意其实就是个数组,有了首元素地址,不就是当成数组来玩嘛
	for (int i = 0; i < 10; i++)//十个元素初始化成0~9
	{
		p[i] = i;//这玩意与*(p+i) = i等价,不懂得可以看阿辉之前关于指针的文章
	}
	return 0;
}

关于malloc函数开辟的空间,它是一块连续的空间,直接当成数组使用一点毛病没有,
它与数组最大的区别在于,malloc开辟的空间是在堆区上的,而数组是在栈区上的。
而堆区上申请的空间须有程序员自己手动释放(Java,Python有垃圾回收机制),否则将造成内存泄漏。其实就是你从我这拿了空间去用,不用了就还给我,别占着茅坑不拉屎,你不用别人也用不了。对于堆区申请的空间的释放,这里要引入下面这个新的函数

✈️free函数

C语言提供了一个函数free,专门用来做动态内存的释放和回收的,函数声明如下:

void free(void* ptr);

对于free函数使用时的注意事项:

  • 对于传给free的参数必须是指向动态开辟内存(也就是是堆区开辟的空间)的起始地址的指针
  • 若传给free的参数是空指针NULLfree函数则什么也不干
  • free只会释放传入的指针指向的空间,并不会给传入指针置空,这会很危险,空间你都还给操作系统了,你这个指针还指向那里,这个指针很野得治,所以free完,指针我们要置空
int mian()
{
	//开辟动态空间
	int* p = (int*)malloc(sizeof(int) * 10);
	//判断动态内存是否开辟成功
	if (p == NULL)
	{
		printf("malloc 开辟空间失败\n");
		return 1;
	}
	/*
	对动态开辟空间的使用
	*/
	//不用啦,释放空间
	free(p);
	p = NULL;
	return 0;
}

👆这段代码就是动态内存的使用的基本模式,先开辟空间,然后判断是否开辟成功,接着使用这块空间,最后不用了把这块空间释放掉

✈️calloc函数

C语言中还提供了calloc函数,同样是进行动态内存分配的,函数声明如下:

void* calloc(size_t num, size_t size);

callocmalloc仅有两点不同

  • 使用上calloc函数需要两个参数,开辟空间为num个大小为size个字节的空间
  • calloc函数开辟的空间会把每个字节全部初始化为0
int main()
{
	int* p = (int*)calloc(10, sizeof(int));
	if (p == NULL)//判断空间是否开辟成功
	{
		printf("calloc开辟空间失败\n");
		return 1;
	}
	for (int i = 0; i < 10; i++)//打印动态内存中的元素
	{
		printf("%d ", p[i]);
	}
	free(p);//空间释放
	p = NULL;//指针置空
	return 0;
}

输出:

0 0 0 0 0 0 0 0 0 0  //十个0

✈️realloc函数

铁子们估计有点不买账了,说好的动态内存分配,讲了这么多也没有体现动态俩字。铁子们别急,realloc函数才是今天的重头戏
C语言提供realloc函数用来使动态内存的使用更加灵活,之前开辟的空间太大或者太小都可以灵活的调整重新分配空间大小
realloc函数的函数声明

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

realloc函数在使用时要注意下面这几点:

  • ptr是要调整的空间的起始地址,ptr得是动态内存开辟的地址,若是ptr为空指针,realloc函数将与malloc函数的实现一样

  • sizerealloc函数调整大小后的内存空间大小

  • realloc函数的返回值是空间调整后的起始地址,空间开辟失败返回空指针

  • realloc函数扩容的空间里面的值并未被初始化,是随机值

  • 这个函数在调整原内存空间大小的基础上,还会将原来内存中的数据移动到新的空间

  • 对于realloc函数调整空间有了两种情况:

    • 情况一:原有空间后有足够空间
      请添加图片描述

    • 情况二:原有空间后无足够空间,原有空间将被释放掉,重新找一块空间
      请添加图片描述

例子:

int main()
{
	int* p = (int*)malloc(5 * sizeof(int));//先用malloc申请5个整型大小的内存空间
	if (p == NULL)//判断是否开辟成功
	{
		printf("malloc开辟空间失败\n");
		return 1;
	}
	int i = 0;
	for (i = 0; i < 5; i++)
	{
		*(p + i) = 1;//把5个整型全部初始化为1
	}
	//不够用了,要再增加5个整型
	int* ptr = (int*)realloc(p, 10 * sizeof(int));
	if (ptr == NULL)
	{
		printf("realloc开辟空间失败\n");
		return 1;
	}
	p = ptr;//空间扩容成功将ptr的地址赋给p
	ptr = NULL;//ptr置空
	for (i = 0; i < 10; i++)
	{
		printf("%d ", *(p + i));
	}
	free(p);
	p = NULL;
	return 0;
}

上述,使用realloc对空间做调整时,一定要新创建一个变量来接收realloc的返回值,因为如果使用指向原空间的起始地址的指针p来接收,一旦realloc开辟空间失败,p将被赋值成NULL,将找不到原来空间造成内存泄漏

🚀在使用动态内存函数时的常见错误

✈️对NULL指针的解引用

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

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

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

✈️对非动态开辟内存使用free释放

void test()
{
    int a = 10;
    int *p = &a;
    free(p);//error——不能对非动态内存进行释放
}

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

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

✈️对同一块动态内存多次释放

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

✈️动态开辟内存忘记释放(内存泄漏)

void test()
{
    int *p = (int *)malloc(100);
    if(NULL != p)
    {
        *p = 20;
    }
}
int main()
{
    test();
    while(1);
}

对于动态开辟的内存一定要记得释放,否则将造成内存泄漏


如果觉得文章对你有帮助的话,还请点赞,关注,收藏支持博主,如有不足还请指点,博主及时改正,感谢大家支持!!!
请添加图片描述

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

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

相关文章

三、C语言中的分支与循环—for循环 (6)

本章分支结构的学习内容如下&#xff1a; 三、C语言中的分支与循环—if语句 (1) 三、C语言中的分支与循环—关系操作符 (2) 三、C语言中的分支与循环—条件操作符 与逻辑操作符(3) 三、C语言中的分支与循环—switch语句&#xff08;4&#xff09;分支结构 完 本章循环结构的…

【SpringBoot框架篇】34.使用Spring Retry完成任务的重试

文章目录 简要1.为什么需要重试&#xff1f;2.添加maven依赖3.使用Retryable注解实现重试4.基于RetryTemplate模板实现重试 简要 Spring实现了一套重试机制&#xff0c;功能简单实用。Spring Retry是从Spring Batch独立出来的一个功能&#xff0c;已经广泛应用于Spring Batch,…

Linux 进程和计划任务管理

一 内核功用&#xff1a;进程管理、内存管理、文件系统、网络功能、驱动程序、安全功能等 1 程序 是一组计算机能识别和执行的指令&#xff0c;运行于电子计算机上&#xff0c;满足人们某种需求的信息化工具 用于描述进程要完成的功能&#xff0c;是控制进程执行的指令集 2…

LeetCode 82:删除排序链表中的重复元素 II

一、题目描述 给定一个已排序的链表的头 head &#xff0c; 删除原始链表中所有重复数字的节点&#xff0c;只留下不同的数字 。返回 已排序的链表 。 示例 1&#xff1a; 输入&#xff1a;head [1,2,3,3,4,4,5] 输出&#xff1a;[1,2,5]示例 2&#xff1a; 输入&#xff1a…

B01、类加载子系统-02

JVM架构图-英文版 中文版见下图&#xff1a; 1、概述类的加载器及类加载过程 1.1、类加载子系统的作用 类加载器子系统负责从文件系统或者网络中加载Class文件,class文件在文件开头有特定的文件标识。ClassLoader只负责class文件的加载,至于它是否可以运行,则由Execution Engi…

炫酷按钮制作(HTML+CSS+Javascript)

实现效果&#xff1a; 当鼠标点击按钮时&#xff1a; 实现代码&#xff1a; <!DOCTYPE html> <html><head><meta charset"utf-8"><title></title><style>div{margin-top: 20px;margin-left: 20px;}.button{border: soli…

力扣热题100道-矩阵篇

矩阵 73.矩阵置零 给定一个 m x n 的矩阵&#xff0c;如果一个元素为 0 &#xff0c;则将其所在行和列的所有元素都设为 0 。请使用 原地 算法**。** 示例 1&#xff1a; 输入&#xff1a;matrix [[1,1,1],[1,0,1],[1,1,1]] 输出&#xff1a;[[1,0,1],[0,0,0],[1,0,1]]示例…

CSS 顶部位置翻转动画

<template><div class"container" mouseenter"startAnimation" mouseleave"stopAnimation"><!-- 旋方块 --><div class"box" :class"{ rotate-hor-top: isAnimating }"><!-- 元素内容 --><…

stable diffusion 基础教程-图生图

界面 图生图大概有以下几个功能: 图生图涂鸦绘制局部绘制局部绘制(涂鸦蒙版)其常用的也就上面四个,接下来逐步讲解。 以图反推提示词 图生图可以根据反推提示词来获取相应图片的提示词,目前3种主流方式,如下: CLIP反推提示词:推导出的文本倾向于自然语言的描述方式,…

Hive详解、配置、数据结构、Hive CLI

一、Hive 认识 1. Hive 应用 问题&#xff1a;公司的经营状况&#xff1f; 主题一&#xff1a;财务现金流指标1.1&#xff1a;净现金流入/流出量指标1.2&#xff1a;现金转换周期预算执行状况指标2.1&#xff1a;预算内成本控制指标2.2&#xff1a;预算与实际支出的差异 主题…

电路笔记 :自激振荡电路笔记 电弧打火机

三极管相关 三极管的形象描述 二极管 简单求解&#xff08;理想&#xff09; 优先导通&#xff08;理想&#xff09; 恒压降 稳压管&#xff08;二极管plus&#xff09; 基础工作模块 理想稳压管的工作特性 晶体管之三极管(“两个二极管的组合” ) 电弧打火机电路 1.闭合开…

docker 安装可视化工具 Portainer 以及 汉化

安装portainer是最新版本&#xff0c;汉化指定版本2.9.1 。如果要安装汉化版&#xff0c;可直接跳转步骤四 一、拉去镜像 安装网址&#xff1a;Install Portainer BE with Docker on Linux - Portainer Documentation docker pull portainer/portainer二、根据portainer镜像创建…

Centos7 安装zabbix6.0.25, agent2

Centos 7 版本&#xff08;不支持yum安装zabbix服务端&#xff0c;只能编译安装服务端&#xff09;zabbix 6.0 版本 版本6.0支持年限&#xff1a; Nginx 1.22.1 版本php 7.2.34 版本mariadb 10.5.19 版本 #关闭防火墙 [rootzabbix ~]# systemctl stop firewalld [rootzabbix…

STL-string

目录 &#x1f4a1;介绍 &#x1f4a1;string的基本操作 &#x1f4a1;string的构造函数 &#x1f4a1;string赋值操作 &#x1f4a1;string字符串拼接 &#x1f4a1;string的查找和替换 &#x1f4a1;string字符串比较 &#x1f4a1;string字符存取 &#x1f4a1;str…

Spark---RDD介绍

文章目录 1.Spark核心编程2.RDD介绍2.1.RDD基本原理2.2 RDD特点1.弹性2.分布式 &#xff1a;数据存储在大数据集群的不同节点上3.数据集 &#xff1a;RDD封装了计算逻辑&#xff0c;并不保存数据4.数据抽象 &#xff1a;RDD是一个抽象类&#xff0c;具体实现由子类来实现5. 不可…

C语言实例_math.h库函数功能及其用法详解

一、前言 数学在计算机编程中扮演着至关重要的角色&#xff0c;C语言的math.h头文件提供了一系列的函数和工具&#xff0c;用于数学计算和常用数学函数的实现。这些函数包括数值运算、三角函数、指数对数函数等&#xff0c;为开发人员提供了强大的数学处理能力。本文将对math.…

字符编码转换

宽窄字符和字符编码的关系 多字节(窄)字符&#xff1a;在C/C中&#xff0c;char是一种数据类型&#xff0c;规定sizeof(char)1&#xff0c;即一个char占用一个字节&#xff0c;1Byte8bit。并没有规定一个char就要与ASCII对应&#xff0c;不过&#xff0c;通常情况下char值与AS…

LeetCode 每日一题 Day 32 ||递归单调栈

2487. 从链表中移除节点 给你一个链表的头节点 head 。 移除每个右侧有一个更大数值的节点。 返回修改后链表的头节点 head 。 示例 1&#xff1a; 输入&#xff1a;head [5,2,13,3,8] 输出&#xff1a;[13,8] 解释&#xff1a;需要移除的节点是 5 &#xff0c;2 和 3 。…

【RocketMQ每日一问】RocketMQ SQL92过滤用法以及原理?

1.生产端 public class SQLProducer {public static int count 10;public static String topic "xiao-zou-topic";public static void main(String[] args) {DefaultMQProducer producer MQUtils.createLocalProducer();IntStream.range(0, count).forEach(i -&g…

【算法】使用位运算解算法题(C++)

文章目录 0. 位运算 基本介绍1. 位运算基本使用 连带题目191.位1的个数338.比特位计数461.汉明距离136.只出现一次的数字260.只出现一次的数字III 2. 使用位运算解决算法题面试题01.01.判定字符是否唯一371.两整数之和137.只出现一次的数字II面试题17.04.消失的数字面试题17.1…