内存地产风云录:malloc、free、calloc、realloc演绎动态内存世界的楼盘开发与交易大戏

欢迎来到白刘的领域   Miracle_86.-CSDN博客

系列专栏  C语言知识

先赞后看,已成习惯

   创作不易,多多支持!

在这个波澜壮阔的内存地产世界中,malloc、free、calloc和realloc四位主角,共同演绎着一场场精彩绝伦的楼盘开发与交易大戏。

目录​​​​​​​

一、为什么要有动态内存分配 

二、malloc和free

2.1 malloc —— 购买土地

2.2 free —— 出售土地 

三、calloc和realloc

3.1 calloc —— 批量购买并初始化土地

3.2 realloc —— 调整土地大小

四、常见的动态内存错误

4.1 对NULL指针解引用

4.2 对动态内存开辟空间的越界访问

4.3 对非动态开辟内存进行free释放

4.4 使用free释放动态开辟内存的一部分

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

4.6 忘记释放(内存泄漏)

五、柔性数组

5.1 柔性数组的特点

5.2 柔性数组的使用 

六、总结C/C++中程序内存区域划分


一、为什么要有动态内存分配 

我们已经掌握的内存开辟方法有:

//变量
int val = 20;//在栈空间上开辟四个字节//数组
char arr[10] = { 0 };//在栈空间上开辟10个字节的连续空间

 但是上述的开辟方法有两个缺点:

1. 开辟的空间大小是有限的。

2. 数组在开辟的时候,必须声明数组的长度,数组空间一旦确定大小就不能调整。

但是对于空间的需求,不仅仅是上述的情况。有时候我们需要的空间在程序运行的时候才能知道,那数组的编译时开辟空间的方式就不能满足了。

所以在C语言中,我们引入了动态内存开辟,可以让程序员自己申请和释放空间,就比较灵活了。

二、malloc和free

如果我们将内存比作地产,那mallocfree就可以非常恰当地比作:购买土地出售土地。

2.1 malloc —— 购买土地

C语言中提供了一个动态内存开辟的函数:

void* malloc(size_t size);

malloc函数向内存申请一块连续可用的空间,并返回指向这块空间的指针。

1. 如果开辟成功,返回一个指向开辟好空间的指针。

2. 如果开辟失败,返回NULL指针。因此malloc的返回值一定要检查。

3. 返回值类型为void*,所以malloc函数并不知道开辟空间的类型,具体在使用的时候程序员自己确定。

4. 如果参数size为0,malloc的行为是标准未定义的,取决于编译器。

2.2 free —— 出售土地 

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

void free(void* ptr);

free函数用来释放动态开辟的内存。

1. 如果参数ptr指向的空间不是动态开辟的,那free的行为是未定义的。

2. 如果参数ptr是NULL指针,则什么也不做。

malloc和free都包含在<stdlib.h>头文件中。

eg:

#include <stdio.h>
#include <stdlib.h>
int main()
{int num = 0;scanf("%d", &num);int arr[num] = { 0 };int* ptr = NULL;ptr = (int*)malloc(num * sizeof(int));if (NULL != ptr)//判断ptr指针是否为空{int i = 0;for (i = 0; i < num; i++){*(ptr + i) = 0;}}free(ptr);//释放ptr所指向的动态内存ptr = NULL;//是否有必要?return 0;
}

 首先,定义了一个整数变量num并初始化为0。然后使用scanf函数从标准输入读取一个整数,并存储在num中。然后,声明了一个长度为num的整数数组arr,并将其所有元素初始化为0。注意,在C99标准之前,这种变长数组(VLA)是不被允许的。但在C99及之后的版本中,这是合法的。变长数组在之前我们也有所讲过:

C语言中的百宝箱——数组(2)-CSDN博客

然后我们定义了一个整数指针ptr并初始化为NULL。 之后使用malloc函数动态分配了num个整数大小的内存,并将返回的指针赋值给ptr。if语句首先检查ptr是否为NULL,以确保内存分配成功。如果成功,则使用一个循环将动态分配的内存的每一个位置初始化为0。紧接着我们使用free函数释放ptr所指向的内存,以避免内存泄漏。

最后我们为什么将ptr设置为空指针,因为此时它是个野指针!如果我们接下来对其操作,将造成严重的后果,再一个就是提高了代码可读性,设置为空指针,提示这块内存已经被释放了。

三、calloc和realloc

callocrealloc也可以有一个比较恰当的比喻:批量购买并且初始化土地(在土地上盖房子)调整土地大小。

3.1 calloc —— 批量购买并初始化土地

C语言中还有一个函数,也是用来动态内存分配,它就是calloc。原型如下:

void* calloc(size_t num, size_t size);

1. 函数的功能是为num个大小为size的元素开辟一块空间,并且把每块空间都初始化为0。

2. 与malloc的区别就是:calloc会在返回地址之前,将申请空间的每个字节都初始化为0。

eg:

#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的作用就是可以做到对动态开辟内存的大小做调整。

函数原型:

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

1. ptr是要调整的地址,size是调整后的新大小。 

2. 返回值为调整之后的内存起始位置。

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

realloc在调整内存空间时存在两种情况:

情况1:原有空间之后有足够大的空间。

情况2:原有空间之后没有足够大的空间。

当是情况1的时候,要扩展内存就直接在原有内存之后直接追加空间,原来空间的数据不发生变化。

当是情况2的时候,原有空间之后没有足够的空间时,扩展的方法是:在堆空间上另找一个合适大小的连续空间来使用。这样函数返回的是一个新的内存地址。

由于上述两种情况,我们在realloc的使用就要注意一些。

#include <stdio.h>
#include <stdlib.h>
int main()
{int* ptr = (int*)malloc(100);if (ptr != NULL){//业务处理}else{return 1;}//扩展容量//代码1 - 直接将realloc的返回值放到ptr中ptr = (int*)realloc(ptr, 1000);//这样可以吗?(如果申请失败会如何?)//代码2 - 先将realloc函数的返回值放在p中,不为NULL,在放ptr中int* p = NULL;p = realloc(ptr, 1000);if (p != NULL){ptr = p;}//业务处理free(ptr);return 0;
}

代码1:

这种直接使用realloc的方式是可行的,但需要注意以下几点:

返回值检查:如果realloc函数调用失败,它会返回NULL。这时,原来的内存块(由ptr指向)也不会被释放,所以需要确保在将realloc的返回值赋给ptr之前检查其返回值是否为NULL

内存泄漏:如果realloc失败并返回NULL,而我们又没有保存原来的ptr的值,那么将失去对原始内存块的引用,从而导致内存泄漏。

代码2:

这种方式更加安全,因为它首先创建了一个新的指针p来保存realloc的返回值。如果realloc成功,p将指向新的内存块,然后你可以安全地将p的值赋给ptr。如果realloc失败,p将为NULL,但ptr仍然指向原来的内存块,因此不会发生内存泄漏。

由于上述代码,我们就不得不简单介绍几个常见的动态内存的错误。

四、常见的动态内存错误

4.1 对NULL指针解引用

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

4.2 对动态内存开辟空间的越界访问

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

4.3 对非动态开辟内存进行free释放

void test()
{int a = 10;int* p = &a;free(p);//ok?
}

4.4 使用free释放动态开辟内存的一部分

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

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

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

4.6 忘记释放(内存泄漏)

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

五、柔性数组

也许你从来没有听说过柔性数组(flexible array)这个概念,但是它确实是存在的。

C99中,结构体中的最后一个元素允许是未知大小的数组,这就叫做“柔性数组”成员。

eg:

typedef struct st_type
{int i;int a[0];//柔性数组成员
}type_a;

有的编译器可能会报错,可以改成:

typedef struct st_type
{int i;int a[];//柔性数组成员
}type_a;

5.1 柔性数组的特点

1. 结构体中的柔性数组前面至少有一个成员。

2. sizeof返回结构体时不包括柔性数组的大小。

3. 包含柔性数组的结构体用malloc函数进行动态开辟,并且分配的内存大小应该大于结构体的大小,以适应柔性数组的大小。

5.2 柔性数组的使用 

#include <stdio.h>  
#include <stdlib.h>  
#include <string.h>  // 定义一个包含柔性数组成员的结构体  
typedef struct {  int count;  double data[]; // 柔性数组成员  
} FlexArray;  int main() {  int array_size = 10; // 假设我们想要一个大小为10的数组  size_t struct_size = sizeof(FlexArray) - sizeof(double[0]); // 计算结构体的固定部分大小  size_t total_size = struct_size + sizeof(double) * array_size; // 计算总大小  // 使用malloc分配内存  FlexArray *p = (FlexArray *)malloc(total_size);  if (p == NULL) {  perror("Memory allocation failed");  return EXIT_FAILURE;  }  // 初始化结构体  p->count = array_size;  for (int i = 0; i < array_size; ++i) {  p->data[i] = i * 1.0; // 假设我们为数组填充一些值  }  // 使用结构体...  for (int i = 0; i < p->count; ++i) {  printf("%f\n", p->data[i]);  }  // 释放内存  free(p);  return 0;  
}

在这个例子中,我们首先计算了结构体的固定部分大小(不包括柔性数组成员),然后加上柔性数组所需的大小,计算出总大小。malloc函数被用来分配所需的总内存大小。

注意,我们在计算结构体固定部分大小时使用了sizeof(double[0]),这是为了确保在计算时不包括柔性数组成员。这个技巧依赖于sizeof对于数组类型返回的是数组的总大小,即使数组的大小是0。

另外,在使用柔性数组成员时,要确保不要试图对结构体使用sizeof来获取完整大小,因为这会返回不包含柔性数组成员的大小。总是根据你的需要动态地计算并分配内存。

最后,别忘了在使用完分配的内存后调用free函数来释放它,以避免内存泄漏。

六、总结C/C++中程序内存区域划分

代码区(Code Area 或 Text Area)

  • 也称为文本段或代码段,它存放程序执行的二进制代码,包括机器指令。这部分内存是只读的,以防止程序意外地修改了它的指令。
  • 编译后的机器码(CPU执行的指令)就放在这一部分内存中。

全局/静态存储区(Global/Static Storage Area)

  • 全局变量和静态变量的存储区域。全局变量包括在函数外部定义的变量,而静态变量包括在函数内部使用static关键字定义的变量以及全局静态变量。
  • 这部分内存的生命周期是整个程序的执行期间。

堆区(Heap Area)

  • 动态内存分配的区域,通常使用malloccallocrealloc在C中分配内存,或者在C++中使用new操作符分配。
  • 程序员负责在不再需要时释放这部分内存,否则会导致内存泄漏。

栈区(Stack Area)

  • 由编译器自动分配和释放,存放函数的参数值、局部变量等。其操作方式类似于数据结构中的栈。
  • 每次函数调用时,都会在栈上为其分配一块内存,用于存储函数的局部变量等。当函数返回时,这块内存会被自动释放。

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

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

相关文章

Linux的网口名字的命名规则

在工作中&#xff0c;偶尔看到有些机器的网口名字是以ethX命令&#xff0c;有些则以enpXsX这种名字命名。网上的资料说的都不太明白,资料也无据可查&#xff0c;很难让人信服。于是决定自己查了下官方的资料和源码&#xff0c;把这些搞清楚。 官方文档&#xff1a;Predictable…

直播视频传输处理技术

流程 在视频直播场景中&#xff0c;从拍摄到手机用户接收的整个过程涉及多个技术环节&#xff1a; 视频采集&#xff1a; 视频源通常来自摄像机或智能手机摄像头&#xff0c;通过捕捉连续的画面生成原始视频信号。 编码压缩&#xff1a; 为了减少数据量以适应网络传输&#x…

【40分钟速成智能风控10】风控大数据体系2

目录 ​编辑 特征工程方法 统计量 离散化 时间周期趋势 交叉项 隐性特征 用户画像 特征工程方法 在模型圈内有这么一句俗话&#xff0c;“特征决定了模型的上限&#xff0c;而算法只是逼近这个上限”&#xff0c;由此可见特征工程在风控建模中的重要程度。特征工程的本…

最大连续1的个数 III

题目链接 最大连续1的个数 III 题目描述 注意点 nums[i] 不是 0 就是 10 < k < nums.length 解答思路 创建一个滑动窗口&#xff0c;保证窗口内翻转0的个数始终不大于k&#xff0c;不断移动窗口的右边界&#xff0c;有以下三种情况&#xff1a; 当右边界的值为1&…

模组硬件通用|ESD静电释放注意事项

当我们在进行接插件操作或者电路板调试时&#xff0c;有时会出现接口损坏或者电路板上的某个IC芯片失效的情况&#xff0c;原因可能仅仅是手触摸到了IC芯片&#xff0c;ESD(Electro-Static discharge 静电释放)导致了损坏。模组作为一个集成电路板&#xff0c;内部含有不同型号…

hertzbeat监控工具部署

目录 参考简介部署docker-compose.ymldocker安装使用portanier部署访问地址默认用户密码 配置SpringBoot程序配置基础信息新增阈值规则新增通知策略 参考 家庭私有云上 Docker 部署 hertzbeat&#xff0c;好用的监控告警系统 官网 简介 hertzbeat是一个拥有强大自定义监控能…

RabbitMQ消息模型之Topic消息模型

Topic消费模型 * 通配符模型 * 生产者必须指定完整且准确的路由key * 消费者可以使用通配符 * *&#xff1a;可以替代一级的任意字符 add.* > add.user add.goods * #&#xff1a;可以替代多级的任意字符 add.# &…

lua基本语法

Lua语法入门 初识lua vi hello.lua print("hello,lua") lua hello.lua 变量和循环 变量 循环 条件控制、函数 条件控制

USB端口

winx&#xff0c;打开设备管理器 名称解释 HS-USB 分类全称传输速率版本超速SSsuper-speed最大速率5Gbps、10Gbps、20GbpsUSB3.0~USB3.2高速HShigh-speed25Mbps-400 Mbps &#xff08;最大480 Mbps&#xff09;USB2.0全速FSfull-speed500Kbps-10Mbps&#xff08;最大12Mbps&…

软考 — 系统架构设计师 - 嵌入式真题

问题1&#xff1a; 可靠度表示系统在规定条件下&#xff0c;规定的时间内不发生失效的概率。 失效率表示系统运行到此时从未出现失效的情况下&#xff0c;单位时间内系统出现失效的概率 问题 2&#xff1a; 动态冗余又称为主动冗余&#xff0c;通过故障检测&#xff0c;故障定…

SAP 计划策略82简介

前面的文章中我们已经测试了很多才策略,10、11、40、50、70、60、63 80策略。 本文将重点说明ATO模式下82策略的使用场景,计划策略82是SAP提供的另一种基于按单生产思想的计划策略,由客户的需求来直接驱动直接生产,是一个按单生产的场景。 1、首先我们先看下系统后台82策略…

回溯算法2s总结

8.回溯算法 回溯算法理论基础 回溯法也可以叫做回溯搜索法&#xff0c;它是一种搜索的方式。回溯是递归的副产品&#xff0c;只要有递归就会有回溯。 回溯的本质是穷举&#xff0c;穷举所有可能&#xff0c;然后选出我们想要的答案 回溯法解决的问题 回溯法&#xff0c;一…

MySQL数据导出导出的三种办法(13/16)

数据导入导出 基本概述 目前常用的有3中数据导入与导出方法&#xff1a; 使用mysqldump工具&#xff1a; 优点&#xff1a; 简单易用&#xff0c;只需一条命令即可完成数据导出。可以导出表结构和数据&#xff0c;方便完整备份。支持过滤条件&#xff0c;可以选择导出部分数据…

VsCode 安装Jupyter Notebook

VsCode 安装Jupyter Notebook 安装 1、打开 VSCode 编辑器&#xff0c;点击界面左端的【扩展】栏&#xff1b; 2、在【搜索框】中输入python&#xff0c;点击第一个Python&#xff0c;检查是否已经安装 python 插件&#xff0c;没安装的点击安装&#xff1b;已安装的继续第3步…

ASUS华硕灵耀Pro14笔记本AMD锐龙版M7400QC,M7600QA原厂Win11系统工厂包下载

恢复华硕灵耀14PRO出厂开箱状态预装OEM系统Windows11工厂模式安装包&#xff0c;带Recovery恢复还原功能 适用型号&#xff1a; M7400QC、M7400QE、M7400QEB M7600QC、M7600QE、M7600QA、M7600QCB 链接&#xff1a;https://pan.baidu.com/s/1dIGRAKJQLQt_JcKbQWFxJg?pwdbn…

GD32 HID键盘矩阵键盘发送数据时,一直发送数据问题处理

这个问题找了两三天,开始并不认为是示例程序的问题,只是感觉是自己代码问题。 这个解决流程大概是: 先调好矩阵键盘=> 调用发送函数。 就是因为调用时,一直发送数据,我也在按键抬起做了操作,始终不行。 最后,发现时示例代码中有个 空闲中断 引起的。 udev->reg…

英伟达高性能芯片供货周期缩短到2-3个月,今年GPU不再紧缺?

戴尔台湾地区总经理Terence Liao近日称&#xff0c;英伟达高性能 AI GPU的交付周期在过去几个月中已从3-4个月缩短到仅2-3个月&#xff0c;进入2024年以来交货等待时间一直在不短缩短&#xff0c;目前的2-3个月已经是英伟达高性能GPU最短的交货期。 英伟达公司正在不断努力提高…

使用easyexcel读取excel并生成sql语句

1、引入pom依赖 <?xml version"1.0" encoding"UTF-8"?> <project xmlns"http://maven.apache.org/POM/4.0.0"xmlns:xsi"http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation"http://maven.apache.org/POM…

【IR-SDE】Image Restoration SDE项目演示运行app.py

背景&#xff1a; code:GitHub - Algolzw/image-restoration-sde: Image Restoration with Mean-Reverting Stochastic Differential Equations, ICML 2023. Winning solution of the NTIRE 2023 Image Shadow Removal Challenge. paper: Official PyTorch Implementations o…

LabVIEW闭环步进电机运动系统设计及精度分析

LabVIEW闭环步进电机运动系统设计及精度分析 在自动化设备不断发展的当代&#xff0c;闭环步进电机以其高精度和可靠性成为了自动化设备的重要组成部分。以LabVIEW软件为核心&#xff0c;结合运动控制卡及驱动器模块&#xff0c;设计并实现了一个闭环步进电机的多轴运动控制系…