【数据结构】动态内存管理函数
动态内存管理
- 为什么存在动态内存管理
- 动态内存函数的介绍
- 🎊malloc
- 补充:perror函数
- 🎊free
- 🎊calloc
- 🎊realloc
- 常见动态内存错误
- 对空指针的解引用操作
- 对动态开辟空间的越界访问
- 对非动态开辟内存使用free释放
- 使用free释放一块动态开辟内存的一部分
- 对同一块动态内存多次释放
- 动态开辟内存忘记释放(内存泄漏)
为什么存在动态内存管理
在此之前,我们开辟内存空间有两种方式。一种是创建一个已知类型的变量。
比如说:
int a=10; //在栈空间上开辟4个字节
向系统申请了4个字节的内存空间。(对于 int型,4个字节它是固定的。)
还有一种是,创建一个数组。
比如说:
int arr[10]; //在栈空间上开辟40个字节的连续空间。
向系统申请了40个字节的内存空间。当这个数组开辟好了空间,没有办法改变它的大小。
对于数组的创建,它的内存开辟方式是比较死板的。
int arr1[10];int arr2[100];
我们创建数组时,在一开始时就会指定数组的大小。arr1的内存空间为40个字节,可以存放10个整型元素。arr2的内存空间为400个字节,可以存放100个整型元素。
但有可能我们在使用数组arr1的时候,需要存放11个数组元素,而没有办法把它边长。
我们可能为了尽可能满足很多情况,而创建一个很大的数组arr2,但在实际使用过程中,我们可能只会存放20个元素,而导致了内存空间的浪费。
所以这样的内存开辟方式它是固定的,是不够灵活的。 不仅仅是上述的情况,有时候我们需要的空间大小在程序运行的时候才能知道,那么数组在编译时开辟空间的方式就不能满足了。 所以,我们需要学会开辟动态内存。
动态内存函数的介绍
malloc
free
calloc
realloc
🎊malloc
malloc函数的原型:
void* malloc(size_t size);
malloc声明在stdlib.h头文件中。
功能:
这个函数向内存申请一块连续可用的空间,并返回指向这块空间的指针。
- 如果开辟成功,则返回指向这块空间的指针。
- 如果开辟失败,则返回一个NULL指针。
- 返回值的类型是 void* ,所以malloc函数并不知道开辟空间的类型,具体在使用的时候使用者自己来决定。
因此,malloc函数的返回值一定要做检查。
举个例子:
原来,我们用数组在栈区开辟内存空间:

现在,我们在堆区动态开辟同样大小的内存空间:

根据malloc函数的原型,我们需要传递一个参数,以字节为单位的内存。
malloc(40)//即开辟了40个字节的内存空间
然后,我们需要一个指针p来指向这块儿开辟好的连续的 内存空间。
但由于 malloc函数的返回值为 void*,即无类型指针,所以我们需要先进行强制转换,将无类型指针转换为整型指针。
因此,
int* p=(int*)malloc(40);
此时我们,开辟的空间在内存中的堆区的空间,但是指向这块空间的指针是放在栈中的,也就是上面例子中的p指针。
如下图所示。

但是,正如我们上面所提到的,我们只是用malloc函数向内存申请开辟40个自己的连续空间,不一定开辟成功。所以我们需要利用指针p进行进一步检验。

若开辟成功,进行访问:

malloc函数 申请的内存空间,但程序退出时,不会主动释放的,需要使用free函数来释放。

补充:perror函数
perror函数(忘的打印输出函数)
来自这篇博客:C语言perror函数详解
🎊free
C语言提供free函数,专门用来做动态内存的释放和回收的。
free函数原型:
void free(void *ptr);
free函数也是声明在头文件<stdlib.h>中的。
free函数用来释放动态开辟的内存。
- 如果参数ptr指向的空间不是动态开辟的,那free函数的行为是未定义的。
- 如果参数ptr是NULL指针,则函数什么事都不做。
🎊calloc
C语言还提供了一个函数叫calloc,calloc函数也用来动态内存分配。
calloc函数的原型:
void* calloc(size_t num,size_t size);
calloc函数的功能为,为num个大小为size的元素开辟一块空间,并且把每个字节初始化为0。
与函数malloc函数的区别只在于calloc会在返回地址之前把申请的每个字节初始化为0。
即:
#include<stdio.h>
#include<stdlib.h>
int main()
{int* p = (int*)calloc(10, sizeof(int));//开辟10个大小为sizeof(int),即4个字节 的空间//判断是否开辟成功if (p == NULL){perror("calloc");return 1;}int i = 0;for (i = 0; i < 10; i++)printf("%d\n", *(p + i));return 0;
}

🎊realloc
realloc函数的出现让动态内存管理更加灵活
有时候我们发现之前申请的内存过小了,有时候我们发现我们申请的内存过大了,所以,在一些时候,我们需要对内存的大小做灵活的调整。那么realloc函数就可以做到对动态开辟内存大小的调整。
realloc函数的原型如下:
void* realloc(void* ptr,size_t size);
其中,ptr是要调整的内存的地址。
size是调整之后的新的内存的大小。
返回值是调整之后内存的起始位置。
这个函数调整原内存空间大小的基础上,还会将原来内存中的数据移动到新的空间。
举个例子:
我们首先使用malloc函数开辟40个字节的空间:
//malloc函数申请空间
int* p = (int*)malloc(40);
if (p == NULL)
{perror("malloc");return 1;
}
//初始化
int i = 0;
for (i = 0; i < 10; i++)*(p + i) = i;
我们想要将这个空间扩大,扩大为80个字节的空间。
于是我们使用realloc函数进行调整。但是内存空间的变化可能有不同的情况。
情况一,后面有足够的空间。
即这样的情形:
要扩展内存就直接在原有内存之后直接追加空间,原来空间的数据不发生变化。

情况二,后面没有足够的空间:
那么就在堆空间上另找一个合适大小的连续空间来使用。这样函数返回的是一个新的内存地址。

//增加一些空间
int *ptr=realloc(p, 80);
if (ptr != NULL)
{p = ptr;
}
在用realloc函数调整动态内存空间时,要注意不能将原来的p指针,来接收realloc(p,80)。
这是因为relloc函数不一定开辟成功新的空间而进行调整,即realloc函数的返回值可能为NULL。
那么p=NULL,本来p维护40个字节的空间。那么这样那个40个字节空间的字节就没有指针维护了。但还没有释放,可能用不到了,但可能找不到了,从而造成内存泄露。
常见动态内存错误
对空指针的解引用操作
当我们用malloc函数在堆上开辟了内存空间,此时会返回一个指针,如果,我们不判断返回值的话,可能就会发生对空指针解引用的错误。
比如:
void test()
{int *p = (int *)malloc(INT_MAX*10);*p = 20;//如果p的值是NULL,就会有问题free(p);
}
- INT_MAX 是在计算机编程中表示有符号整型(signed integer)所能存储的最大值。

如上如,p是空指针。那么就发生了对空指针的解引用操作的错误。
对动态开辟空间的越界访问
这个道理和在栈上申请空间是一样的道理。
在堆上申请空间,超过范围越界访问就会报错。

void test()
{int i = 0;int *p = (int *)malloc(10*sizeof(int));if(NULL == p){exit(-1);}for(i=0; i<=10; i++){*(p+i) = i;//当i是10的时候越界访问}free(p);
}
对非动态开辟内存使用free释放
对于在栈上开辟的空间,却用free来释放…头脑不清醒可能会用free来释放吧…

使用free释放一块动态开辟内存的一部分
例如,下面的一段代码,p没有释放掉动态内存起始位置的那块空间。
这种错误就是在写代码的过程中,起始指针跑偏了,但自己可能没有意识到。

所以,一块连续的空间必须重头释放,一次性全部释放完。
对同一块动态内存多次释放
例如下面这段代码,它释放了两次,就会出现报错。

更好的习惯是,当我们释放完一段空间后,将指针p设置为空指针。
void test()
{int *p=(int *)malloc(100);free(p);p=NULL;free(p);//此时就什么事就没有了
}
动态开辟内存忘记释放(内存泄漏)
void test5()
{int* p = (int*)malloc(100);if (NULL != p){*p = 20;}
}int main()
{test5();while(1);return 0;
}
例如,这段代码,在主函数中,我调用test5()函数时,p指针在堆上申请了一块空间,但是函数调用完毕后,出了这个test5()函数,局部变量指针p已经销毁了。
但是,这在堆上开辟的100个空间还在占用。
并且出了这个函数,我们已经找不到这块空间的地址了,程序while(1)还在继续。
我们想用,但是不知道这块空间的起始地址,所以我们用不上。同样的,我们想要释放,我们还是释放不了。这就造成了内存泄漏。
所以它只有当程序结束后,才会自动释放。
相关文章:
【数据结构】动态内存管理函数
动态内存管理 为什么存在动态内存管理动态内存函数的介绍🎊malloc补充:perror函数🎊free🎊calloc🎊realloc 常见动态内存错误对空指针的解引用操作对动态开辟空间的越界访问对非动态开辟内存使用free释放使用free释放一…...
TVM框架学习笔记
TVM是陈天齐等人一个开源的深度学习编译器栈,用于优化和部署机器学习模型到各种硬件后端。它支持多种前端框架,如TensorFlow、PyTorch、ONNX等,并且可以在不同的硬件平台上运行,包括CPU、GPU和专用加速器。官方文档: Apache TVM Documentation — tvm 0.20.dev0 documenta…...
neo4j-community-5.26.0 install in window10
在住处电脑重新配置一下neo4j, 1.先至官方下载 Neo4j Desktop Download | Free Graph Database Download Neo4j Deployment Center - Graph Database & Analytics 2.配置java jdk jdk 21 官网下载 Java Downloads | Oracle 中国 path: 4.查看java -version 版本 5.n…...
macbook安装go语言
通过brew来安装go语言 使用brew命令时,一般都会通过brew search看看有哪些版本 brew search go执行后,返回了一堆内容,最下方展示 If you meant "go" specifically: It was migrated from homebrew/cask to homebrew/core. Cas…...
LCD液晶屏的工作原理以及背光模组
LCD液晶屏的工作原理以及背光模组 液晶屏工作原理 像素点的主要结构背光模组 LCD液晶屏主要由两部分组成,液晶屏和背光模组。背光模组提供均匀稳定的光源,液晶屏控制光线的传播路径,是屏幕显示设定的图像。 液晶屏 LCD的核心是两片玻璃之间…...
es数据同步
Logstash 是 Elastic 技术栈中的一个技术,它是一个数据采集引擎,可以从数据库采集数据到 ES 中。可以通过设置 自增 ID 主键 或 更新时间 来控制数据的自动同步: 自增 ID 主键:Logstatsh 会有定时任务,如果发现有主键…...
maven的打包插件如何使用
默认的情况下,当直接执行maven项目的编译命令时,对于结果来说是不打第三方包的,只有一个单独的代码jar,想要打一个包含其他资源的完整包就需要用到maven编译插件,使用时分以下几种情况 第一种:当只是想单纯…...
【Elasticsearch】中数据流需要配置索引模板吗?
是的,数据流需要配置索引模板。在Elasticsearch中,数据流(Data Streams)是一种用于处理时间序列数据的高级结构,它背后由多个隐藏的索引组成,这些索引被称为后备索引(Backing Indices࿰…...
Controller 层优化四步曲
Controller 层优化四步曲 前言 在开发过程中,Controller 层作为系统与外界交互的桥梁,承担着接收请求、解析参数、调用业务逻辑、处理异常等职责。 然而,随着业务复杂度的增加,Controller 层的代码往往会变得臃肿且难以维护。 …...
Java后端之AOP
AOP:面向切面编程,本质是面向特定方法编程 引入依赖: <dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-aop</artifactId></dependency>示例:记录…...
中文输入法方案
使用了三年的自然码双拼,毫无疑问是推荐使用双拼输入法。 三年积累下来的习惯是: 1 自然码方案 2 空格出字 字母选字 直到如今,想要做出改变,是因为这样的方案带来的痛点: 1 使用空格出字就无法使用辅助码&#…...
Elasticsearch中的度量聚合:深度解析与实战应用
在大数据和实时分析日益重要的今天,Elasticsearch以其强大的搜索和聚合能力,成为了众多企业和开发者进行数据分析和处理的首选工具。本文将深入探讨Elasticsearch中的度量聚合(Metric Aggregations),展示其如何在数据分…...
Julius AI 人工智能数据分析工具介绍
Julius AI 是一款由 Casera Labs 开发的人工智能数据分析工具,旨在通过自然语言交互和强大的算法能力,帮助用户快速分析和可视化复杂数据。这款工具特别适合没有数据科学背景的用户,使数据分析变得简单高效。 核心功能 自然语言交互&#x…...
乌兰巴托的夜---音乐里的故事
不知道你有没有听过《乌兰巴托的夜》。 《乌兰巴托的夜Ulaanbaatariin udesh》其实是蒙古国的流行歌曲,1980年代创作,以蒙古国首都乌兰巴托(Ulaanbaatar)的夜晚为背景,表现恋爱中的男女青年爱情的抒情小夜曲ÿ…...
机器学习day4
自定义数据集 使用pytorch框架实现逻辑回归并保存模型,然后保存模型后再加载模型进行预测 import numpy as np import torch import torch.nn as nn import torch.optim as optimizer import matplotlib.pyplot as pltclass1_points np.array([[2.1, 1.8],[1.9, 2…...
LVGL+FreeRTOS实战项目:智能健康助手(蓝牙模块篇)
HC-05 蓝牙模块简介 功能:支持串口通信的蓝牙模块,广泛应用于无线数据传输。支持 AT 指令配置。 接口:UART 通信,默认波特率为 9600bps。 应用:无线调试、数据传输、无线控制等。 硬件连接 HC-05 引脚功能STM32 连…...
如何部署 Flask 应用程序到生产环境?
在生产环境中部署 Flask 应用程序需要考虑稳定性、安全性、可扩展性和性能。以下是 Flask 应用的常见生产部署方法及最佳实践: 1. 选择部署方式 常见的 Flask 生产环境部署方式包括: 部署方式适用场景说明Gunicorn Nginx适合中小型项目轻量级…...
【愚公系列】《循序渐进Vue.js 3.x前端开发实践》029-组件的数据注入
标题详情作者简介愚公搬代码头衔华为云特约编辑,华为云云享专家,华为开发者专家,华为产品云测专家,CSDN博客专家,CSDN商业化专家,阿里云专家博主,阿里云签约作者,腾讯云优秀博主&…...
Redis学习之哨兵二
一、API 1.sentinel masters:展示被监控的主节点状态及相关的统计信息 2.sentinel master <master name>:展示指定的主节点的状态以及相关的统计信息 3.sentinel slaves <master name>:展示指定主节点的从节点状态以及相关的统计信息 4.sentinel sentinels <mas…...
【Linux基础指令】第二期
本期博客的主题依旧是 "基础指令" ; 上一期的基础指令链接: 【Linux基础指令】第一期-CSDN博客 ,话不多说,正文开始: 一、Linux的指令 1.cp 拷贝功能: cp [stc] [dest] # 将 src文件 拷贝到…...
SpringBoot的细节问题
一、重点填空 Pivotal 团队在原有 spring 框架的基础上开发了全新的Spring Boot框架。Spring Boot框架在开发过程中大量使用 约定优于配置 的思想来摆脱框架中各种复杂的手动配置。Spring Boot 2.1.3版本要求 Java 8 及以上版本的支持。Spring Boot2.1.3版本框架官方声明支持的…...
JavaScript系列(46)-- WebGL图形编程详解
JavaScript WebGL图形编程详解 🎨 今天,让我们深入探讨JavaScript的WebGL图形编程。WebGL是一种基于OpenGL ES的JavaScript API,它允许我们在浏览器中渲染高性能的2D和3D图形。 WebGL基础概念 🌟 💡 小知识ÿ…...
MySQL(表空间)
开始前先打开此图配合食用 MySQL表空间| ProcessOn免费在线作图,在线流程图,在线思维导图 InnoDB 空间文件中的页面管理 后面也会持续更新,学到新东西会在其中补充。 建议按顺序食用,欢迎批评或者交流! 缺什么东西欢迎评论!我都…...
C26.【C++ Cont】动态内存管理和面向对象的方式实现链表
🧨🧨🧨🧨🧨🧨🧨🧨🧨除夕篇🧨🧨🧨🧨🧨🧨🧨🧨🧨 目录 1.知识回顾…...
使用numpy自定义数据集 使用tensorflow框架实现逻辑回归并保存模型,然后保存模型后再加载模型进行预
1. 引言 逻辑回归(Logistic Regression)是一种常见的分类算法,广泛应用于二分类问题。在本篇博客中,我们将使用numpy生成一个简单的自定义数据集,并使用TensorFlow框架构建和训练逻辑回归模型。训练完成后,…...
Java Web 开发基础介绍
Java学习资料 Java学习资料 Java学习资料 一、引言 在当今数字化时代,Web 应用无处不在。Java 凭借其强大的功能、良好的跨平台性和丰富的开发框架,成为 Web 开发领域的热门选择之一。Java Web 开发允许开发者构建动态、交互式的 Web 应用程序&#x…...
求解旅行商问题的三种精确性建模方法,性能差距巨大
文章目录 旅行商问题介绍三种模型对比求解模型1决策变量目标函数约束条件Python代码 求解模型2决策变量目标函数约束条件Python代码 求解模型3决策变量目标函数约束条件Python代码 三个模型的优势与不足 旅行商问题介绍 旅行商问题 (Traveling Salesman Problem, TSP) 是一个经…...
C++:多继承习题3
题目内容: 声明一个时间类Time,时间类中有3个私有数据成员(Hour,Minute,Second)和两个公有成员函数(SetTime和PrintTime)。要求: (1) SetTime根据传递的3个参数为对象设置时间; &a…...
低代码系统-产品架构案例介绍、得帆云(八)
产品名称 得帆云DeCode低代码平台-私有化 得帆云DeMDM主数据管理平台 得帆云DeCode低代码平台-公有云 得帆云DePortal企业门户 得帆云DeFusion融合集成平台 得帆云DeHoop数据中台 名词 概念 云原生 指自己搭建的运维平台,区别于阿里云、腾讯云 Dehoop 指…...
python Flask-Redis 连接远程redis
当使用Flask-Redis连接远程Redis时,首先需要安装Flask-Redis库。可以通过以下命令进行安装: pip install Flask-Redis然后,你可以使用以下示例代码连接远程Redis: from flask import Flask from flask_redis import FlaskRedisa…...



