欣赏动态之美,不如欣赏C语言实现动态内存管理之美 ! ! !
本篇会加入个人的所谓‘鱼式疯言’
❤️❤️❤️鱼式疯言:❤️❤️❤️此疯言非彼疯言
而是理解过并总结出来通俗易懂的大白话,
我会尽可能的在每个概念后插入鱼式疯言,帮助大家理解的.
可能说的不是那么严谨.但小编初心是能让更多人能接受我们这个概念 !!!
前言
在本篇文章中,小编将带大家领略动态内存管理的魅力💖💖💖
- 为什么要有动态内存分配
- malloc和free
- calloc和realloc
- 柔性数组
- 总结C/C++中程序内存区域划分
话不多说,我们直接上菜吧 💕💕💕
一.为什么要有动态内存分配
我们已经掌握的内存开辟⽅式有
int val = 20;//在栈空间上开辟四个字节
char arr[10] = {0};//在栈空间上开辟10个字节的连续空间
但是上述的开辟空间的⽅式有两个特点:
• 空间开辟⼤⼩是固定的。
• 数组在申明的时候,必须指定数组的⻓度,数组空间⼀旦确定了⼤⼩不能调整
但是对于空间的需求,不仅仅是上述的情况。有时候我们需要的空间⼤⼩在程序运⾏的时候才能知
道,那数组的编译时开辟空间的⽅式就不能满⾜了。
C语⾔引⼊了动态内存开辟,让程序员⾃⼰可以申请和释放空间,就⽐较灵活了。
二. malloc和free
2.1 malloc
C语⾔提供了⼀个函数叫 malloc
malloc 函数⽤来动态内存分配,是作为内存空间的 开垦机
原型如下:
2.1.1举个栗子
#include <stdlib.h>
#include<stdio.h>
int main()
{int* p = (int*)malloc(10*sizeof(int));if (p == NULL){perror("malloc");return 1;}int i = 0;//使用 - 给数组赋值for (i = 0; i < 10; i++){*(p + i) = i;}//打印for (i = 0; i < 10; i++){printf("%d ", *(p + i));}//释放空间free(p);p = NULL;return 0;
}
这个函数向内存申请⼀块连续可⽤的空间,并返回指向这块空间的指针。
• 如果开辟成功,则返回⼀个指向开辟好空间的指针。
<1> 错误示例
#include <stdlib.h>
#include<stdio.h>
int main()
{int* p = (int*)malloc(1000000000000);if (p == NULL){perror("malloc");return 1;}int i = 0;//使用 - 给数组赋值for (i = 0; i < 10; i++){*(p + i) = i;}//打印for (i = 0; i < 10; i++){printf("%d ", *(p + i));}return 0;
}
宝子们是不是很疑惑,这是怎么回事呢?🤔🤔🤔
那是因为 malloc 在开辟空间的时候是有可能开辟失败的
• 如果开辟失败,则返回⼀个 NULL 指针,因此malloc的返回值⼀定要做检查。
其他情况:
• 返回值的类型是 void* ,所以malloc函数并不知道开辟空间的类型,具体在使⽤的时候使⽤者⾃⼰来决定。
• 如果参数 size 为0,malloc的⾏为是标准是未定义的,取决于编译器。
2.2 free
2.2.1举个栗子
free 函数⽤来释放开辟的动态内存。
• 如果参数 ptr 指向的空间不是动态开辟的,那free函数的⾏为是未定义的。
• 如果参数 ptr 是 NULL 指针,则函数什么事都不做。
#include <stdlib.h>
#include<stdio.h>
int main()
{int* p = (int*)malloc(10*sizeof(int));//开辟空间if (p == NULL){perror("malloc");return 1;}//释放空间free(p);p = NULL;return 0;
}
有空间的开辟就有空间的释放,有人问为什么要把 p 置为空指针NULL
小伙伴可以带着疑问继续往下看哦😊😊😊
三. calloc和realloc
3.1 calloc
C语⾔还提供了⼀个函数叫 calloc , calloc 函数也⽤来动态内存分配。原型如下:
• 函数的功能是为 num 个⼤⼩为 size 的元素开辟⼀块空间,并且把空间的每个字节初始化为 0。
• 与函数 malloc 的区别只在于 calloc 会在返回地址之前把申请的空间的每个字节初始化为全
3.1.1.举个栗子
#include <stdio.h>
#include <stdlib.h>
int main()
{int* p = (int*)calloc(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函数来完成任务。
以上这些都是固定的内存开辟, 还不足以灵活。
小编为什么会这样说呢! ! !
是的,是的,我们还有超级秘密武器。
请友友向下看
3.2 realloc
• realloc函数的出现让动态内存管理更加灵活。
• 有时会我们发现过去申请的空间太⼩了,有时候我们⼜会觉得申请的空间过⼤了,那为了合理的时候内存,我们⼀定会对内存的⼤⼩做灵活的调整。
那 realloc 函数就可以做到对动态开辟内存⼤⼩的调整。
函数原型如下:
3.2.1.举个栗子
#include <stdio.h>
#include <stdlib.h>
int main()
{int* ptr = (int*)malloc(10*sizeof(int));if (ptr != NULL){for (int i = 0; i < 10; i++){*(ptr+i) = i;printf("%d", ptr[i]);}}else{perror("malloc");return 1;}//扩展容量//代码1 - 直接将realloc的返回值放到ptr中//ptr = (int*)realloc(ptr, 1000);//这样可以吗?(如果申请失败会如何?)//代码2 - 先将realloc函数的返回值放在p中,不为NULL,在放ptr中int* p = NULL;p = realloc(ptr, 15*sizeof(int));if (p != NULL){ptr = p;for (int i = 10; i < 15; i++){*(ptr + i) = i;}}printf("\n");for (int i = 0; i < 15; i++){printf("%d ", *(ptr + i));}//业务处理free(ptr);return 0;
}
这时我们小爱同学就有疑问了🤔🤔🤔
为什么realloc在内存中到底是怎么加长 或是 减短的呢? ? ?
答案见下图😍😍😍`
• ptr 是要调整的内存地址
• size 调整之后新⼤⼩
• 返回值为调整之后的内存 起始位置 。
• 这个函数调整原内存空间⼤⼩的基础上,还会将原来内存中的数据移动到 新 的空间。
• realloc在调整内存空间的是存在两种情况:
。情况1:原有空间之后有⾜够⼤的空间
。情况2:原有空间之后没有⾜够⼤的空间
鱼式疯言
上面的插图和代码充分说明了一点
我们不可以将 realloc 函数得到的变化后的空间直接放在我们需要的指针变量中
我们就有必要加上一个临变量来判断其是否为 NULL ! ! !
3.3 realloc的特殊使用
有没有友友们想过,如果我往 realloc 传第一个参数,传的是NULL的空指针呢,那会怎么样呢🤔🤔🤔
不妨我们来试试吧
#include <stdlib.h>
#include<stdio.h>
int main()
{//int* p = (int*)malloc(10 * sizeof(int));//等效于下面这个int* p = (int*)realloc(NULL,10*sizeof(int));//开辟空间if (p == NULL){perror("realloc");return 1;}for (int i = 0; i < 10; i++){*(p + i) = i;printf("%d ", *(p + i));}//释放空间free(p);p = NULL;return 0;}
四. 柔性数组
4.1 柔性数组的特点:
• 结构中的柔性数组成员前⾯必须⾄少⼀个其他成员。
• sizeof 返回的这种结构⼤⼩不包括柔性数组的内存。
• 包含柔性数组成员的结构⽤ malloc () 函数进⾏内存的动态分配,并且分配的内存应该⼤于结构的⼤⼩,以适应柔性数组的预期⼤⼩。
4.1.1.举个栗子
#include<stdio.h>
typedef struct st_type
{int i;int a[0];//柔性数组成员
}type_a;
int main()
{printf("%d\n", sizeof(type_a));//输出的是4return 0;
}
这个栗子充分说明了柔性数组不占内存
鱼式疯言
我理解的柔性数组
- 在结构体中
- 最后一个成员
- 未知大小的数组
4.2 柔性数组的使⽤
4.2.2.举个栗子
//柔性数组
#include<stdio.h>
#include<stdlib.h>
struct MyStruct
{int a;char b;int arr[];
};
int main()
{struct MyStruct* p = (struct MyStruct*)malloc(sizeof(struct MyStruct)+10*sizeof(int));if (p == NULL){perror("malloc");return 1;}for (int i = 0; i < 10; i++){p->arr[i] = i;printf("%d ", p->arr[i]);}p->a = 10;p->b = 'd';printf("\n%d\n", p->a);printf("%c\n", p->b);struct MyStruct* art= (struct MyStruct*)realloc(p,sizeof(struct MyStruct) + 20 * sizeof(int));if (art!=NULL){p = art;for (int i = 10; i < 15; i++){p->arr[i] = i;}}for (int i = 0; i < 15; i++){printf("%d ", p->arr[i]);}free(p);p = NULL;return 0;
}
这样柔性数组成员 arr,相当于获得了 15 个整型元素的连续空间。
4.3 柔性数组的优势
4.3.1举两个栗子
<1>
#include<stdio.h>
struct St
{char c;int n;int arr[0];//柔性数组//[]里面可以放随机数字,也可放置为空
};
int main()
{//struct St s = {0};//printf("%d\n", sizeof(struct St));struct St* ps = (struct St*)malloc(sizeof(struct St) + 10 * sizeof(int));if (ps == NULL){perror("malloc");return 1;}ps->c = 'w';ps->n = 100;int i = 0;for (i = 0; i < 10; i++){ps->arr[i] = i;}//数组空间不够struct St* ptr = realloc(ps, sizeof(struct St) + 15 * sizeof(int));if (ptr != NULL){ps = ptr;}else{perror("realloc");return 1;}//...继续使用for (i = 10; i < 15; i++){ps->arr[i] = i;}for (i = 0; i < 15; i++){printf("%d ", ps->arr[i]);}printf("\n%d\n", ps->n);printf("%c\n", ps->c);//释放free(ps);ps = NULL;return 0;
}
<2>
struct St
{char c;int n;int* arr;
};int main()
{//struct St s = {0};//printf("%d\n", sizeof(struct St));struct St* ps = (struct St*)malloc(sizeof(struct St));if (ps == NULL){perror("malloc");return 1;}ps->c = 'w';ps->n = 100;ps->arr = (int*)malloc(10*sizeof(int));if (ps->arr == NULL){perror("malloc-2");return 1;}//使用int i = 0;for (i = 0; i < 10; i++){ps->arr[i] = i;}//数组空间不够int* ptr = (int*)realloc(ps->arr, 15*sizeof(int));if (ptr == NULL){perror("realloc");return 1;}else{ps->arr = ptr;}//使用for (i = 10; i < 15; i++){ps->arr[i] = i;}for (i = 0; i < 15; i++){printf("%d ", ps->arr[i]);}printf("\n%d\n", ps->n);printf("%c\n", ps->c);//释放free(ps->arr);ps->arr = NULL;free(ps);ps = NULL;return 0;
}
上述 <1> 和 <2> 可以完成同样的功能,
但是 ⽅法1 的实现有两个好处:
第⼀个好处是:⽅便内存释放
如果我们的代码是在⼀个给别⼈⽤的函数中,
你在⾥⾯做了⼆次内存分配,并把整个结构体返回给⽤⼾。
⽤⼾调⽤free可以释放结构体,
但是⽤⼾并不知道这个结构体内的成员也需要free,所以你不能指望⽤⼾来发现这个事。所以,如果我们把结构体的内存以及其成员要的内存⼀次性分配好了.
并返回给⽤⼾⼀个结构体指针,⽤⼾做⼀次free就可以把所有的内存也给释放掉。
第⼆个好处是:这样有利于访问速度.
连续的内存有益于提⾼访问速度,也有益于减少内存碎⽚。
(其实,我个⼈觉得也没多⾼了,
反正你跑不了要⽤做偏移量的加法来寻址)
鱼式疯言
柔性数组的优势就不言而喻了吧,但最最must一点是什么???
小伙伴们知道么😊😊😊
当然是 释放内存 和 指针置空(NULL) 咯! ! !
第一:
我们要明确一点 malloc / calloc / realloc 申请的空间都是在内存堆区(下面有讲解)申请的
第二:在 堆区 申请的空间如果不主动释放,出了作用域是不会销毁的。
第三:
释放的方式:
1.free 主动释放
2.直到程序结束,才由 操作系统 回放
第四:
我们free释放的该指针指向的空间,而不是该该指针变量本身的地址
故我们需要将该指针也得置为空(NULL)
五. 总结C/C++中程序内存区域划分
C/C++程序内存分配的⼏个区域:
- 栈区(stack):在执⾏函数时,函数内局部变量的存储单元都可以在栈上创建,函数执⾏结束时
这些存储单元⾃动被释放。栈内存分配运算内置于处理器的指令集中,效率很⾼,
但是分配的内存容量有限。
栈区主要存放运⾏函数⽽分配的局部变量、函数参数、返回数据、返回地址等。 - 堆区(heap):⼀般由程序员分配释放,
若程序员不释放,程序结束时可能由OS(操作系统)回收 。
分配⽅式类似于链表。 - 数据段(静态区)(static)存放全局变量、静态数据。程序结束后由系统释放。
- 代码段:存放函数体(类成员函数和全局函数)的⼆进制代码。
总结
在本篇文章中
小编主要带着宝子们学习了哪些呢,让我们梳理梳理吧💖💖💖
- 为什么要有动态内存分配,它的意义和作用体现在何处?
- malloc 和 free 这两个一头一尾是怎么样唱双簧的?
- calloc 和 realloc 具体然后使用和以及他们的特殊性
- 柔性数组的特点以及他所自带的独特的优势
- 总结C/C++中程序内存区域是怎么样子划分的,他们分别存着哪些类型的数据
💖💖💖本次博文就到这里了,感觉各位小伙伴的赏脸品读小编写的拙作哦,
如果觉得小编写的还不错的咱可支持三关下,不妥当的咱评论区指正
希望我的文章能给各位家人们带来哪怕一点点的收获就是小编创作的最大动力💖💖💖
相关文章:

欣赏动态之美,不如欣赏C语言实现动态内存管理之美 ! ! !
本篇会加入个人的所谓‘鱼式疯言’ ❤️❤️❤️鱼式疯言:❤️❤️❤️此疯言非彼疯言 而是理解过并总结出来通俗易懂的大白话, 我会尽可能的在每个概念后插入鱼式疯言,帮助大家理解的. 可能说的不是那么严谨.但小编初心是能让更多人能接受我们这个概念 !࿰…...
from pycocotools.coco import COCO报错
需要注意的是,自己的系统是windows还是linux的系统: windows系统的安装: pip install pycocotools-windows linux的系统安装: pip install pycocotools 别用错了命令哦!...

CentOS服务自启权威指南:手动启动变为开机自启动(以Jenkins服务为例)
前言 CentOS系统提供了多种配置服务开机自启动的方式。本文将介绍其中两种常见的方式, 一种是使用Systemd服务管理器配置,不过,在实际中,如果你已经通过包管理工具安装的,那么服务通常已经被配置为Systemd服务&#…...

第二百零一回 介绍一个三方包open_settings
文章目录 1. 概念介绍2 使用方法3 代码与效果3.1 示例代码3.2 运行效果 4. 经验分享 我们在上一章回中介绍了Form Widget相关的内容,本章回中将介绍Form系列组件的验证与提交功能.闲话休提,让我们一起Talk Flutter吧。 1. 概念介绍 我们在这里说的的验…...

iview Table实现跨页勾选记忆功能以及利用ES6的Map数据结构实现根据id进行对象数组的去重
因为iview Table组件的勾选是选中当前页的所有数据,当我们切到别的页面时,会发送请求给后端,这个时候就会刷新我们之前页码已经选中的数据。现在有个需求就是,在我们选择不同页码的数据勾选中之后,实现跨页勾选记忆功能,就是说已经打钩了的数据,不管切到哪一页它都是打钩…...

【Spring 源码】 贯穿 Bean 生命周期的核心类之 AbstractAutowireCapableBeanFactory
🚀 作者主页: 有来技术 🔥 开源项目: youlai-mall 🍃 vue3-element-admin 🍃 youlai-boot 🌺 仓库主页: Gitee 💫 Github 💫 GitCode 💖 欢迎点赞…...

漏洞复现-某友UFIDA NC系统某接口未授权访问漏洞(附漏洞检测脚本)
免责声明 文章中涉及的漏洞均已修复,敏感信息均已做打码处理,文章仅做经验分享用途,切勿当真,未授权的攻击属于非法行为!文章中敏感信息均已做多层打马处理。传播、利用本文章所提供的信息而造成的任何直接或者间接的…...
树莓派5安装opencv
1 建立虚拟环境 参考网站 https://www.piwheels.org/faq.html#venv 虚拟环境建立过程: To create a virtual environment: $ sudo apt install virtualenv python3-virtualenv -y $ virtualenv -p /usr/bin/python3 testpip sudo apt install virtualenv pytho…...

【测试开发】Python+Django实现接口测试工具
PythonDjango接口自动化 引言: 最近被几个公司实习生整自闭了,没有基础,想学自动化又不知道怎么去学,没有方向没有头绪,说白了其实就是学习过程中没有成就感,所以学不下去。出于各种花里胡哨的原因…...

从 MQTT、InfluxDB 将数据无缝接入 TDengine,接入功能与 Logstash 类似
利用 TDengine Enterprise 和 TDengine Cloud 的数据接入功能,我们现在能够将 MQTT、InfluxDB 中的数据通过规则无缝转换至 TDengine 中,在降低成本的同时,也为用户的数据转换工作提供了极大的便捷性。由于该功能在实现及使用上与 Logstash 类…...
友元c++
#include <iostream> #include <string> using namespace std; class Lovers//爱人关系,基类 { public: Lovers(string theName);// void kiss(Lovers *lover); void ask(Lovers *lover, string something); protected: string name; friend cla…...

java: 错误: 不支持发行版本 6
文章目录 背景一、问题二、问题排查三、最终解决方案 背景 我本地安装的jdk版本是jdk 17,在项目父工程中配置的版本是8版本,每次我启动项目时都会报错。 一、问题 二、问题排查 首先我排查了父工程pom.xml中的配置,配置的是8版本࿰…...
qml刷新C++中的QImage图像
第一步:重写QQuickImageProvider类 #include <QQuickImageProvider>class CQuickImagePainter : public QQuickImageProvider { public:CQuickImagePainter();QImage requestImage(const QString&id, QSize *, const QSize &);QPixmap requestPixm…...

IJCAI 2024 International Joint Conference on Artificial Intelligence
目录 1、 重要1.1 官网:1.2 提交网址:1.3 模板 (latex & word) 2、 Call for Papers2.1 Important Dates2.2 Details 3、 注意事项4 New in 20245 Simplified procedure for resubmission information6、 Submission Process …...

使用Python Flask搭建Web问答应用程序并发布到公网远程访问
使用Python Flask搭建web问答应用程序框架,并发布到公网上访问 文章目录 使用Python Flask搭建web问答应用程序框架,并发布到公网上访问前言1. 安装部署Flask并制作SayHello问答界面2. 安装Cpolar内网穿透3. 配置Flask的问答界面公网访问地址4. 公网远程…...
android 13.0 app应用安装白名单
1.概述 在13.0系统rom定制化开发中,客户需求要实现应用安装白名单功能,在白名单之中的应用可以安装,其他的app不准安装,实现一个 控制app安装的功能,这需要从app安装流程入手就可以实现功能 PMS就是负责管理app安装的,功能就添加在这里就可以了,接下来看具体实现这个功能…...

SSL证书HTTPS保护服务
SSL证书属于数字证书的其中一种,广泛用于https协议,从而可以让数据传输在加密前提下完成,确保HTTPS网络安全是申请SSL证书必要工作。 SSL证书是主要用于https是一种加密协议,仔细观察网站地址会发现目前主流的网址前面都会有http…...

快速认识什么是:Docker
Docker,一种可以将软件打包到容器中并在任何环境中可靠运行的工具。但什么是容器以及为什么需要容器呢?今天就来一起学快速入门一下Docker吧!希望本文对您有所帮助。 假设您使用 Cobol 构建了一个在某种奇怪风格的 Linux 上运行的应用程序。您…...

c语言青蛙跳台阶
"青蛙跳台阶"问题是一个经典的动态规划问题,经常被用来解释动态规划的基本概念。问题的描述是:假设一只青蛙可以跳上1级或2级台阶,如果有n级台阶,那么青蛙有多少种跳法。 在C语言中,我们可以使用动态规划来…...

IntelliJ IDEA 2023.3 最新版如何试用?IntelliJ IDEA 2023.3 最新版试用方法
🌷🍁 博主猫头虎(🐅🐾)带您 Go to New World✨🍁 🦄 博客首页——🐅🐾猫头虎的博客🎐 🐳 《面试题大全专栏》 🦕 文章图文…...
web vue 项目 Docker化部署
Web 项目 Docker 化部署详细教程 目录 Web 项目 Docker 化部署概述Dockerfile 详解 构建阶段生产阶段 构建和运行 Docker 镜像 1. Web 项目 Docker 化部署概述 Docker 化部署的主要步骤分为以下几个阶段: 构建阶段(Build Stage):…...

linux之kylin系统nginx的安装
一、nginx的作用 1.可做高性能的web服务器 直接处理静态资源(HTML/CSS/图片等),响应速度远超传统服务器类似apache支持高并发连接 2.反向代理服务器 隐藏后端服务器IP地址,提高安全性 3.负载均衡服务器 支持多种策略分发流量…...

高等数学(下)题型笔记(八)空间解析几何与向量代数
目录 0 前言 1 向量的点乘 1.1 基本公式 1.2 例题 2 向量的叉乘 2.1 基础知识 2.2 例题 3 空间平面方程 3.1 基础知识 3.2 例题 4 空间直线方程 4.1 基础知识 4.2 例题 5 旋转曲面及其方程 5.1 基础知识 5.2 例题 6 空间曲面的法线与切平面 6.1 基础知识 6.2…...

令牌桶 滑动窗口->限流 分布式信号量->限并发的原理 lua脚本分析介绍
文章目录 前言限流限制并发的实际理解限流令牌桶代码实现结果分析令牌桶lua的模拟实现原理总结: 滑动窗口代码实现结果分析lua脚本原理解析 限并发分布式信号量代码实现结果分析lua脚本实现原理 双注解去实现限流 并发结果分析: 实际业务去理解体会统一注…...

深入解析C++中的extern关键字:跨文件共享变量与函数的终极指南
🚀 C extern 关键字深度解析:跨文件编程的终极指南 📅 更新时间:2025年6月5日 🏷️ 标签:C | extern关键字 | 多文件编程 | 链接与声明 | 现代C 文章目录 前言🔥一、extern 是什么?&…...

Redis数据倾斜问题解决
Redis 数据倾斜问题解析与解决方案 什么是 Redis 数据倾斜 Redis 数据倾斜指的是在 Redis 集群中,部分节点存储的数据量或访问量远高于其他节点,导致这些节点负载过高,影响整体性能。 数据倾斜的主要表现 部分节点内存使用率远高于其他节…...

10-Oracle 23 ai Vector Search 概述和参数
一、Oracle AI Vector Search 概述 企业和个人都在尝试各种AI,使用客户端或是内部自己搭建集成大模型的终端,加速与大型语言模型(LLM)的结合,同时使用检索增强生成(Retrieval Augmented Generation &#…...

Docker 本地安装 mysql 数据库
Docker: Accelerated Container Application Development 下载对应操作系统版本的 docker ;并安装。 基础操作不再赘述。 打开 macOS 终端,开始 docker 安装mysql之旅 第一步 docker search mysql 》〉docker search mysql NAME DE…...

代码规范和架构【立芯理论一】(2025.06.08)
1、代码规范的目标 代码简洁精炼、美观,可持续性好高效率高复用,可移植性好高内聚,低耦合没有冗余规范性,代码有规可循,可以看出自己当时的思考过程特殊排版,特殊语法,特殊指令,必须…...

如何应对敏捷转型中的团队阻力
应对敏捷转型中的团队阻力需要明确沟通敏捷转型目的、提升团队参与感、提供充分的培训与支持、逐步推进敏捷实践、建立清晰的奖励和反馈机制。其中,明确沟通敏捷转型目的尤为关键,团队成员只有清晰理解转型背后的原因和利益,才能降低对变化的…...