【C语言进阶】动态内存管理
创始人
2024-05-26 05:12:38
0

在这里插入图片描述

👦个人主页:@Weraphael
✍🏻作者简介:目前是C语言学习者
✈️专栏:C语言航路
🐋 希望大家多多支持,咱一起进步!😁
如果文章对你有帮助的话
欢迎 评论💬 点赞👍🏻 收藏 📂 加关注


目录

  • 一、动态内存分配的由来
  • 二、动态内存函数的介绍
      • 2.1 malloc函数
      • 2.2 free函数
      • 2.3 calloc函数
      • 2.4 realloc函数
  • 三、常见的动态内存错误
      • 3.1 对NULL指针的解引用操作
      • 3.2 对动态开辟空间的越界访问
      • 3.3 对非动态开辟内存使用free释放
      • 3.4 使用free释放一块动态开辟内存的一部分
      • 3.5 对同一块动态内存多次释放
      • 3.6 动态开辟内存忘记释放

一、动态内存分配的由来

我们已经张伟的内存开辟方式有:

int a = 3;//在内存空间开辟4个字节
char a[10] = {0};//在内存开辟10个字节且连续的空间

但是上述的开辟空间的方式有两个特点:

  1. 空间开辟大小是固定
  2. 数组再声明的时候,必须指定数组的长度,它所需要的内存在编译时分配。

但是对于空间的需求,不仅仅是上述的情况。因为有时候我们需要的空间大小在程序运行的时候才知道,那么以上的方式就不能满足了。而动态内存分配恰好能在需要时随时开辟,不需要的时候随时释放,且分配的大小就是程序要求的大小

二、动态内存函数的介绍

2.1 malloc函数

在这里插入图片描述

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

  • 如果开辟成功,则返回一个指向开辟好的空间的指针
  • 如果开辟失败,则返回一个NULL指针(空指针),因此malloc函数的返回值一定要做检查
    *返回值是void*,表示的是无具体类型的指针,这说明malloc函数并不知道开辟空间的类型,具体是在使用的时候让使用者来决定
  • 如果参数size为0,malloc的行为是标准未定义的,取决用编译器

malloc函数实例】

strerror讲解传送门 -> 点击此处

#include 
#include  //malloc函数所需头文件
#include   //errno所需头文件
#include //strerror所需头文件int main()
{//假设向内存申请40个字节,用来存放整型int* p = (int*)malloc(40);//判断返回值是否为空指针if (p == NULL){//说明开辟内存空间失败了,那就显示为什么失败了printf("%s\n", strerror(errno));return 1;}//否则返回值就不为空指针//假设存放数据1~10for (int i = 0; i < 10; i++){*(p + i) = 1 + i;}//打印for (int i = 0; i < 10; i++){printf("%d ", *(p + i));}return 0;
}

在这里插入图片描述

2.2 free函数

malloc函数实例中,代码会存在隐患。malloc函数既然在内存中申请了空间,用完了就应该还给操作系统。所以C语言提供了另外一个函数free专门用做动态内存的释放和回收的

在这里插入图片描述

解读:

  • free函数是用来释放内存块
  • free函数无返回值
  • 参数ptr是指向先前使用 malloc、calloc 或 realloc 分配的内存块的指针。
  • 如果参数ptr指向的空间不是动态开辟的,那么free函数的行为就是未定义的
  • 如果参数ptrNULL指针,则函数不会做任何事。

free函数实例(完善malloc函数实例)】

#include 
#include  //malloc函数所需头文件
#include   //errno所需头文件
#include //strerror所需头文件int main()
{//假设向内存申请40个字节,用来存放整型int* p = (int*)malloc(40);//判断返回值是否为空指针if (p == NULL){//说明开辟内存空间失败了,那就显示为什么失败了printf("%s\n", strerror(errno));return 1;}//否则返回值就不为空指针//假设存放数据1~10for (int i = 0; i < 10; i++){*(p + i) = 1 + i;}//打印for (int i = 0; i < 10; i++){printf("%d ", *(p + i));}//释放申请的内存free(p);p = NULL;return 0;
}

释放完空间后,p = NULL的原因:
我们可以通过F10调试来观察
释放前:我们将数据1~10存放到内存了
在这里插入图片描述

释放后:我们发现,p在释放后还指向释放前的空间

在这里插入图片描述
所以,为了防止日后非法访问,我们就将p=NULL

2.3 calloc函数

C语言还提供一个函数calloccalloc函数也用来动态内存分配

在这里插入图片描述

解读:

  • 参数num是开辟元素的个数
  • 参数size是每个元素的大小
  • callocmalloc区别在于calloc会在返回地址之前把申请的空间的每个字节初始化为0

【calloc函数实例】

calloc会将申请的空间全部初始化为0

#include 
#include 
#include 
#include int main()
{//向内存申请10个元素,每个元素都是int类型int* p = calloc(10, sizeof(int));//判断p是否为空指针if (p == NULL){printf("%s\n", strerror(errno));return 1;}//否则不是空指针for (int i = 0; i < 10; i++){printf("%d ", *(p + i));}//用完释放空间freefree(p);p = NULL;return 0;
}

在这里插入图片描述

2.4 realloc函数

  • realloc函数的出现让动态内存管理更加灵活
  • 有时候会发现过去申请的空间太小了,有时候又会觉得申请的空间过大,那为了合理的使用内存,就要对内存的大小做灵活的调整。使用realloc函数就的功能就是调整动态开辟内存大小

在这里插入图片描述

解读:

  • 参数ptr是要调整的内存地址
  • 参数size为调整之后的新大小
  • 返回值为调整之后的内存的起始地址
  • 这个函数调整原内存空间大小的基础上,还会将原来内存中的数据移动带新的空间

realloc函数在调整内存空间的时候存在两种情况:

  1. 当原有空间之后有足够大的空间,要扩展内存就直接在原有内存之后直接追加空间,原来空间的数据不发生变化,返回的是原来的地址
  2. 当原有空间之后没有足够大的空间,扩展的方法:在堆空间上另找一个合适大小的连续空间来使用。这样函数返回的是一个新内存的地址

【realloc函数实例】

#include 
#include 
#include 
#include int main()
{//假设向内存申请20个字节来存放5个整型int* p = (int*)malloc(20);//判断p是否为空指针if (p == NULL){printf("%s\n", strerror(errno));return 1;}//否则p就不是空指针//接下来就将5个整型全部赋值成1for (int i = 0; i < 5; i++){*(p + i) = 1;}//那么接下来假设我还要再开辟5个空间(realloc)int* ptr = (int*)realloc(p, 40);if (ptr != NULL)//说明扩容成功{p = ptr;}//打印10个整型for (int i = 0; i < 10; i++){printf("%d ", *(p + i));}//释放空间(free)free(p);p = NULL;return 0;
}

在这里插入图片描述

三、常见的动态内存错误

3.1 对NULL指针的解引用操作

#include 
#include int main()
{int* p = (int*)malloc(40);*p = 20;free(p);p = NULL;return 0;
}
  • p = NULL,对于空指针解引用可能会导致程序异常终止或拒绝服务。

3.2 对动态开辟空间的越界访问

#include 
#include int main()
{//向内存申请10个整型的空间int* p = (int*)malloc(10 * sizeof(int));if (p == NULL){return 1;}for (int i = 0; i <= 10; i++){*(p + i) = i;}free(p);p = NULL;return 0;
}
  • 当指针指向的范围超过数组的范围时,p就是野指针,而野指针是没有访问权限的地址,会造成非法访问

3.3 对非动态开辟内存使用free释放

#include 
#include int main()
{int a = 10;int* p = &a;free(p);p = NULL;return 0;
}
  • 这个就是free函数的概念,free的参数指向的空间一定是动态开辟的,比如:mallocrealloccalloc。而这里的p指向的是一个变量的地址

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

#include 
#include int main()
{int* p = (int*)malloc(40);p++;free(p);p = NULL;return 0;
}
  • 要知道不管是前置++还是后置++,它都是会有副作用的。p++后,p就会指向p++的位置,然后使用free释放只会释放p后面的内存空间,只释放了一部分
    在这里插入图片描述

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

#include 
#include int main()
{int* p = (int*)malloc(40);if (p == NULL){return 1;}free(p);free(p);return 0;
}
  • 对一次free回收了p申请的内存空间,此时的p就是野指针,而野指针是没有访问地址的权限,若再一次使用free来回收内存空间,可能会造成非法访问

3.6 动态开辟内存忘记释放

#include 
#include int main()
{int* p = (int*)malloc(40);if (p == NULL){return 1;}*p = 20;return 0;
}
  • 使用完内存空间后,并没有释放(free)内存空间,会导致内存泄漏

相关内容

热门资讯

【NI Multisim 14...   目录 序言 一、工具栏 🍊1.“标准”工具栏 🍊 2.视图工具...
银河麒麟V10SP1高级服务器... 银河麒麟高级服务器操作系统简介: 银河麒麟高级服务器操作系统V10是针对企业级关键业务...
不能访问光猫的的管理页面 光猫是现代家庭宽带网络的重要组成部分,它可以提供高速稳定的网络连接。但是,有时候我们会遇到不能访问光...
AWSECS:访问外部网络时出... 如果您在AWS ECS中部署了应用程序,并且该应用程序需要访问外部网络,但是无法正常访问,可能是因为...
Android|无法访问或保存... 这个问题可能是由于权限设置不正确导致的。您需要在应用程序清单文件中添加以下代码来请求适当的权限:此外...
北信源内网安全管理卸载 北信源内网安全管理是一款网络安全管理软件,主要用于保护内网安全。在日常使用过程中,卸载该软件是一种常...
AWSElasticBeans... 在Dockerfile中手动配置nginx反向代理。例如,在Dockerfile中添加以下代码:FR...
AsusVivobook无法开... 首先,我们可以尝试重置BIOS(Basic Input/Output System)来解决这个问题。...
ASM贪吃蛇游戏-解决错误的问... 要解决ASM贪吃蛇游戏中的错误问题,你可以按照以下步骤进行:首先,确定错误的具体表现和问题所在。在贪...
月入8000+的steam搬砖... 大家好,我是阿阳 今天要给大家介绍的是 steam 游戏搬砖项目,目前...