【C语言】动态内存管理(malloc,free,calloc,realloc)-- 详解
一、动态内存分配
定义:动态内存分配 (Dynamic Memory Allocation) 就是指在程序执行的过程中,动态地分配或者回收存储空间的分配内存的方法。动态内存分配不像数组等静态内存分配方法那样,需要预先分配存储空间,而是由系统根据程序的需要即时分配,且分配的大小就是程序要求的大小。
目前掌握的两种开辟内存的方式:
// 在栈空间上开辟四个字节
int val = 20; // 在栈空间上开辟10个字节的连续空间
char arr[10] = {0};
上述的开辟空间的方式有两个特点:
- 空间开辟大小是固定的。
- 数组在声明时必须指定数组的长度,在编译时会分配其所需要的内存空间。
存在动态内存开辟的原因:对于空间的需求,不仅仅是上述的情况。有时我们需要的空间大小在程序运行的时候才能知道,那数组的编译时开辟空间的方式就不能满足了,这时我们就需要动态内存开辟来解决问题。
二、动态内存函数的介绍
1、malloc 函数
void* malloc(size_t size);
- malloc 是 C 语言提供的一个动态内存开辟的函数,该函数向内存申请一块连续可用的空间,并返回指向这块空间的指针。
- 如果开辟成功,则返回一个指向开辟好空间的指针。
- 如果开辟失败,则返回一个 NULL 指针,因此 malloc 的返回值一定要做检查。
- 返回值的类型是 void* ,所以 malloc 函数并不知道开辟空间的类型,具体在使用的时候使用者自己来决定。
- 如果参数 size 为 0,malloc 的行为是标准是未定义的,取决于编译器。
cplusplus.com/reference/cstdlib/malloc/?kw=malloc
2、free 函数
![]()
void free (void* ptr);
- free 函数用来释放动态开辟的内存。
- 如果参数 ptr 指向的空间不是动态开辟的,那 free 函数的行为是未定义的。
- 如果参数 ptr 是 NULL 指针,则 free 函数将不会执行任何动作。
⚠ 注意:
- 使用完之后一定要记得使用 free 函数释放所开辟的内存空间。
- 使用指针指向动态开辟的内存,使用完并 free 之后一定要记得将其置为空指针。
cplusplus.com/reference/cstdlib/free/
【演示】
#include <stdio.h>
#include <stdlib.h>int main()
{int arr[10]; // 开辟10个整型空间int* p = (int*)malloc(10*sizeof(int)); // 动态开辟10个大小为int的空间if (p == NULL) // 判断p指针是否为空{perror("main"); // main: 错误信息return 0;}for (int i = 0; i < 10; i++){*(p + i) = i;}for (int i = 0; i < 10; i++){printf("%d ", p[i]);}// 回收空间free(p); // 释放p指针所指向的动态内存p = NULL; // 需要手动置为空指针return 0;
}
【1】为什么 malloc 前面要进行强制类型转换呢?
int* p = (int*)malloc(10*sizeof(int));为了和 int* p 类型相呼应,所以要进行强制类型转换。如果把强制转换删掉,其实也不会有什么问题。但是因为有些编译器要求强转,所以最好进行一下强转,避免不必要的麻烦。
【2】为什么 free 之后,一定要把 p 置为空指针?
因为 free 之后那块开辟的内存空间已经不存在了,它的功能只是把开辟的空间回收掉,但是 p 仍然还指向那块内存空间的起始位置,这并不合理。所以需要使用 p = NULL 把它置成空指针。
3、calloc 函数
void* calloc(size_t num, size_t size);
- 函数的功能是为 num 个大小为 size 的元素开辟一块空间,并且把空间的每个字节初始化为 0。
- 与函数 malloc 的区别只在于 calloc 会在返回地址之前把申请的空间的每个字节初始化为全 0。
cplusplus.com/reference/cstdlib/calloc/
#include <stdio.h>
#include <stdlib.h>int main()
{int* p = (int*)calloc(10, sizeof(int)); // 开辟10个大小为int的空间,40if (p == NULL){return 1;}for (int i = 0; i < 10; i++){printf("%d ", *(p + i));}free(p);p = NULL;return 0;
}


结论:说明 calloc 会对内存进行初始化,把空间的每个字节初始化为 0 。如果我们对于申请的内存空间的内容,要求其初始化,我们就可以使用 calloc 函数来轻松实现。
4、realloc 函数
void* realloc (void* ptr, size_t size);
- realloc 函数,让动态内存管理更加灵活。用于重新调整之前调用 malloc 或 calloc 所分配的 ptr 所指向的内存块的大小,可以对动态开辟的内存进行大小的调整。
- ptr 为指针要调整的内存地址。
- size 为调整之后的新大小。
- 返回值为调整之后的内存起始位置,请求失败则返回空指针。
- realloc 函数在调整原内存空间大小的基础上,还会将原来内存中的数据移动到新的空间。
realloc 在调整内存空间的是存在三种情况:
- 原有空间之后有足够大的空间。
- 原有空间之后没有足够大的空间。
- realloc 有可能找不到合适的空间来调整大小。
第三种情况,如果 realloc 找不到合适的空间,就会返回空指针。如果想让它增容,它却存在返回空指针的危险,怎么办?
不要拿指针直接接收 realloc,可以使用临时指针判断一下。
#include <stdio.h> #include <stdlib.h>int main() {int* p = (int*)calloc(10, sizeof(int));if (p == NULL){perror("main");return 1;}for (int i = 0; i < 10; i++){*(p + i) = 5;}// 此时,这里需要 p 指向的空间更大,需要 20 个int的空间int* ptmp = (int*)realloc(p, 20*sizeof(int));// 如果ptmp不等于空指针,再把p交付给它if (ptmp != NULL){p = ptmp;}free(p);p = NULL; }
当要调整的内存地址为 NULL 时,realloc 的功能相当于 malloc。
int* p = (int*)realloc(NULL, 40); // 这里功能类似于malloc,就是直接在堆区开辟40个字节
三、常见的动态内存错误
1、对NULL指针的解引用操作
// error - 错误演示
#include <stdlib.h>
#include <stdio.h>void test()
{int* p = (int*)malloc(9999999999);*p = 20; // 对空指针进行解引用操作,非法访问内存free(p);return 0;
}
// 正确代码
#include <stdlib.h>
#include <stdio.h>int main()
{int* p = (int*)malloc(9999999999);if (p == NULL) // 对malloc函数的返回值做判空处理{perror("main")return 1;}for (int i = 0; i < 10; i++){*(p + i) = i; // 对空指针进行解引用操作,非法访问内存}return 0;
}
2、对动态开辟空间的越界访问
堆上开辟的空间是有范围的。
// error
#include <stdio.h>
#include <stdlib.h>int main()
{int* p = (int*)malloc(10*sizeof(int)); // 申请10个整型的空间if (p == NULL){perror("main");return 1;}for (int i = 0; i < 40; i++) // 越界访问 - 指针p只管理10个整型的空间,根本无法访问40个{*(p + i) = i;}free(p);p = NULL;return 0;
}
注意:为了防止越界访问,使用空间时一定要注意开辟的空间大小。
3、对非动态开辟内存使用free释放
#include <stdio.h>
#include <stdlib.h>int main()
{int arr[10] = {0};int* p = arr;free(p); // 使用free释放非动态开辟的空间 - errorp = NULL;return 0;
}
4、使用free释放一块动态开辟内存的一部分
void test()
{int *p = (int *)malloc(100);p++;free(p); // p不再指向动态内存的起始位置
}
此时 free(p) 就会出问题,释放的是后面的空间。不能从一块动态开辟的内存空间的某一部分释放,必须从头开始释放!
这么写会导致 p 只释放了后面的空间,并不知道这块空间的起始位置,这样会存在内存泄露的风险。
⚠ 注意:释放内存空间的时候一定要从头开始释放。
5、对同一块动态内存多次释放
void test()
{int *p = (int *)malloc(100);free(p);// p = NULL;free(p);//重复释放
}
应该在第一次释放后紧接着将 p 置为空指针。
6、动态开辟内存忘记释放(内存泄漏)
#include <stdio.h>
#include <stdlib.h>void test()
{int* p = (int*)malloc(100);if (p == NULL){return;}// 这里忘记释放了
}int main()
{test();free(p); // 此时释放不了了,并不知道这块空间的起始位置在哪p = NULL;
}
忘记释放不再使用的动态开辟的空间会造成内存泄漏。注意 : 动态开辟的空间一定要释放,并且正确释放。
C / C++ 中程序内存区域划分
- 栈区(stack):在执行函数时,函数内局部变量的存储单元都可以在栈上创建,函数执行结束时这些存储单元自动被释放。栈内存分配运算内置于处理器的指令集中,效率很高,但是分配的内存容量有限。 栈区主要存放运行函数而分配的局部变量、函数参数、返回数据、返回地址等。
- 堆区(heap):一般由程序员分配释放,若程序员不释放,程序结束时可能由 OS 回收 。分配方式类似于链表。
- 数据段(静态区)(static):存放全局变量、静态数据。程序结束后由系统释放。
- 代码段:存放函数体(类成员函数和全局函数)的二进制代码。
实际上普通的局部变量是在栈区分配空间的,栈区的特点是在上面创建的变量出了作用域就销毁。但是被 static 修饰的变量存放在数据段(静态区),数据段的特点是在上面创建的变量,直到程序结束才销毁,所以 生命周期变长 。
相关文章:
【C语言】动态内存管理(malloc,free,calloc,realloc)-- 详解
一、动态内存分配 定义:动态内存分配 (Dynamic Memory Allocation) 就是指在程序执行的过程中,动态地分配或者回收存储空间的分配内存的方法。动态内存分配不像数组等静态内存分配方法那样,需要预先分配存储空间,而是由系统根据程…...
adb 命令
1.adb shell dumpsys activity top | find "ACTIVITY" 查看当前运行的activity包名 2.adb shell am start -n 包名/页面名 打开应用的页面 3.查看将要启动或退出app的包名 adb shell am monitor 只有在启动或退出的时候才会打印 4.查看当前启动应用的包名 ad…...
Linux 进程间通信——消息队列
一、消息队列的原理 消息队列提供了一种从一个进程向另一个进程发送一个数据块的方法。每个数据块都被认为含有一个类型,接收进程可以独立接收含有不同类型值得数据库。 消息实际上是一个数据块,这个数据块是一个结构体,结构体由自己命名。消…...
ChatGPT在智能娱乐和游戏互动中的应用如何?
在智能娱乐和游戏互动领域,ChatGPT具有广泛的应用潜力,可以为用户带来更丰富、个性化和有趣的体验。从虚拟角色和游戏情节到实时互动和玩家支持,ChatGPT可以在多个方面为游戏产业带来创新和改变。 **1. **虚拟角色和NPC互动**:Ch…...
【Ubuntu】systemd 及其工具
什么是 systemd systemd 是一个用于管理 Linux 系统启动过程和系统服务的初始化系统。它是现代 Linux 发行版中广泛采用的初始化系统,负责启动和管理操作系统的各个组件。 systemd 的设计目标是提高系统启动速度、优化资源管理和提供更强大的服务管理功能。它引入…...
抖音seo矩阵系统源代码开发部署分享
一、 开发步骤分享 抖音SEO矩阵系统源代码开发部署分享,需要经验丰富的开发人员和服务器管理人员,以下是大致的步骤: 确定你需要的功能和设计,确定开发人员和设计师的角色和任务分配,以及开发进度和计划。 确定服务器…...
FastJson在Java后端方面解析使用(二)
JSON现在常用来做前后端数据交互,两个蝴蝶飞只是简单的对JSON做一下讲解和简单使用。关于JSON,我还了解的远远不够。由于本人经验有限,嘴皮子不溜,所以学术性,概念性,底层性的知识点暂时不做介绍。文章中有错误之处…...
PyTorch深度学习实战(5)——计算机视觉基础
PyTorch深度学习实战(5)——计算机视觉基础 0. 前言1. 图像表示2. 将图像转换为结构化数组2.1 灰度图像表示2.2 彩色图像表示3 利用神经网络进行图像分析的优势小结系列链接0. 前言 计算机视觉是指通过计算机系统对图像和视频进行处理和分析,利用计算机算法和方法,使计算机…...
ImageReader保存图片转 opencvmat
目录 ImageReader 直接保存图片,没成功,格式是yuv420,需要转换 转opencv nv21保存图片,测试ok rgb888 data保存图片: ImageReader 直接保存图片,没成功,格式是yuv420,需要转换 …...
【vue3+ts项目】配置husky+配置commitlint
上一篇文章中配置了eslint校验代码工具 【vue3ts项目】配置eslint校验代码工具,eslintprettierstylelint 1、配置husky 每次手动执行命令才能格式化代码,如果有人没有格式化就提交到远程仓库,这个规范就起不到作用了,所有需要强…...
html实现iframe全屏
前言 html浏览器全屏操作,基于jquery iframe全屏、指定标签全屏 实现 css /** 全屏*/ .lay-dbclick-box{position: relative;width: 100%;height: 100%; } .lay-dbclick-screen{position: absolute;top: 0;left: 0;width: 100%;height: 100%;z-index: 999999999…...
【es6】中的Generator
Generator 一、Generator 是什么?1.1 与普通函数写法不一样,有两个不同 二、Generator 使用2.1 书写方法 三、yield语句3.1 yield和return3.2 注意事项3.3 yield*语句3.4 yield*应用 四、next方法4.1参数4.2 运行逻辑 五、异步解决方案六、Generator相关…...
桥梁安全监测方法和内容是什么?
桥梁安全监测方法和内容是什么?桥梁监测是保障桥梁安全和稳定的重要手段。随着科技的进步,桥梁监测技术和设备不断完善,监测内容也越来越全面。本文万宾科技小编将为大家介绍桥梁安全监测的方法和内容,以期帮助大家更好地了解这一领域。 桥梁…...
prometheus部署及钉钉告警集成Grafana
1、准备工作 安装包 📎alertmanager-0.23.0.linux-amd64.tar.gz 📎node_exporter-1.3.1.linux-amd64.tar.gz 📎prometheus-webhook-dingtalk-1.4.0.linux-amd64.tar.gz 服务端口 Prometheus 9090 node_exporter 9100 alertmanager …...
Java百度提前批面试题
今天分享百度提前批的 Java 后端开发面经,整体上考察的点挺多的,主要重点考察了网络i/o、网络协议、linux系统、mysql,Java 问的不多,可能是百度的后端开发的语言不是主要以 Java 为主,所以重点看面试者的计算机基础是…...
Go语言中的Oop面向对象
Go In OOp 一、 Go是面向对象的吗?二、Structs Instead of Classes 结构体 - OOP in Go三、 Composition Instead of Inheritance 组合嵌套 - OOP in Go1.Composition by embedding structs2. Embedding slice of structs 四、Polymorphism 多态 - OOP in Go1. Polymorphism u…...
Duplicate keys detected: ‘1‘. This may cause an update error.
报错 Duplicate keys detected: ‘1’. This may cause an update error. 注释: 检测到重复密钥:‘1’。这可能会导致更新错误。 解决 首先判断是因为for循环导致的,检查是否出现重复。 笔者是同一个页面两处for循环导致...
C++(8.21)c++初步
1.斐波那契: #include <iostream> #include<iomanip>using namespace std;int main() {cout << "Hello World!" << endl;int a[10];for(int i0;i<10;i){if(0i||1i){a[i]1;}elsea[i]a[i-1]a[i-2];cout <<setw(4) <&l…...
【【Verilog典型电路设计之log函数的Verilog HDL设计】】
Verilog典型电路设计之log函数的Verilog HDL设计 log函数是一种典型的单目计算函数,与其相应的还有指数函数、三角函数等。对于单目计算函数的硬件加速器设计一般两种简单方法:一种是查找表的方式;一种是使用泰勒级数展开成多项式进行近似计算。这两种方式在设计方…...
数字放大(C++)
系列文章目录 1.进阶的卡沙_睡觉觉觉得的博客-CSDN博客 2. 数1的个数_睡觉觉觉得的博客-CSDN博客 3. 双精度浮点数的输入输出_睡觉觉觉得的博客-CSDN博客 4. 足球联赛积分_睡觉觉觉得的博客-CSDN博客 5. 大减价(一级)_睡觉觉觉得的博客-CSDN博客 6. 小写字母的判断_睡觉觉觉得…...
业务系统对接大模型的基础方案:架构设计与关键步骤
业务系统对接大模型:架构设计与关键步骤 在当今数字化转型的浪潮中,大语言模型(LLM)已成为企业提升业务效率和创新能力的关键技术之一。将大模型集成到业务系统中,不仅可以优化用户体验,还能为业务决策提供…...
Java 语言特性(面试系列2)
一、SQL 基础 1. 复杂查询 (1)连接查询(JOIN) 内连接(INNER JOIN):返回两表匹配的记录。 SELECT e.name, d.dept_name FROM employees e INNER JOIN departments d ON e.dept_id d.dept_id; 左…...
微信小程序之bind和catch
这两个呢,都是绑定事件用的,具体使用有些小区别。 官方文档: 事件冒泡处理不同 bind:绑定的事件会向上冒泡,即触发当前组件的事件后,还会继续触发父组件的相同事件。例如,有一个子视图绑定了b…...
阿里云ACP云计算备考笔记 (5)——弹性伸缩
目录 第一章 概述 第二章 弹性伸缩简介 1、弹性伸缩 2、垂直伸缩 3、优势 4、应用场景 ① 无规律的业务量波动 ② 有规律的业务量波动 ③ 无明显业务量波动 ④ 混合型业务 ⑤ 消息通知 ⑥ 生命周期挂钩 ⑦ 自定义方式 ⑧ 滚的升级 5、使用限制 第三章 主要定义 …...
pam_env.so模块配置解析
在PAM(Pluggable Authentication Modules)配置中, /etc/pam.d/su 文件相关配置含义如下: 配置解析 auth required pam_env.so1. 字段分解 字段值说明模块类型auth认证类模块,负责验证用户身份&am…...
深入理解JavaScript设计模式之单例模式
目录 什么是单例模式为什么需要单例模式常见应用场景包括 单例模式实现透明单例模式实现不透明单例模式用代理实现单例模式javaScript中的单例模式使用命名空间使用闭包封装私有变量 惰性单例通用的惰性单例 结语 什么是单例模式 单例模式(Singleton Pattern&#…...
【单片机期末】单片机系统设计
主要内容:系统状态机,系统时基,系统需求分析,系统构建,系统状态流图 一、题目要求 二、绘制系统状态流图 题目:根据上述描述绘制系统状态流图,注明状态转移条件及方向。 三、利用定时器产生时…...
汇编常见指令
汇编常见指令 一、数据传送指令 指令功能示例说明MOV数据传送MOV EAX, 10将立即数 10 送入 EAXMOV [EBX], EAX将 EAX 值存入 EBX 指向的内存LEA加载有效地址LEA EAX, [EBX4]将 EBX4 的地址存入 EAX(不访问内存)XCHG交换数据XCHG EAX, EBX交换 EAX 和 EB…...
【JavaWeb】Docker项目部署
引言 之前学习了Linux操作系统的常见命令,在Linux上安装软件,以及如何在Linux上部署一个单体项目,大多数同学都会有相同的感受,那就是麻烦。 核心体现在三点: 命令太多了,记不住 软件安装包名字复杂&…...
MySQL用户和授权
开放MySQL白名单 可以通过iptables-save命令确认对应客户端ip是否可以访问MySQL服务: test: # iptables-save | grep 3306 -A mp_srv_whitelist -s 172.16.14.102/32 -p tcp -m tcp --dport 3306 -j ACCEPT -A mp_srv_whitelist -s 172.16.4.16/32 -p tcp -m tcp -…...









