动态内存管理

简介: 动态内存管理

💕"痛苦难以避免,而磨难可以选择。"-->村上春树💕

作者:Mylvzi

文章主要内容:动态内存管理

前言:为什么要进行动态内存管理-->之前开辟内存的方式对于内存的使用过于局限;

一.动态内存函数的介绍

所有的与动态内存分配有关的函数都包含于<stdlib.h>这个头文件之中

1.malloc函数-->动态内存开辟函数

作用:在堆区之中申请size字节大小的空间,并返回开辟内存空间的起始地址;

注意:

1.在申请内存的过程中有可能申请失败,如果申请失败,会返回NULL;(一定要进行判断,对空指针的解引用是非法的)

2.malloc的返回值是void*,使用者可以根据自身需求进行强制类型转换;

3.动态开辟的内存并不会自动销毁(还给操作系统),销毁有两种方式,使用free函数释放内存空间,程序退出;

2.free函数-->动态内存释放函数

作用:释放之前已经被动态内存函数(malloc,calloc,realoc)开辟的内存空间,将内存空间还给操作系统

注意:

1.只能释放动态开辟的内存,不能释放在栈区开辟的内存:

2.释放之后,要及时对释放空间的起始地址置于NULL,避免野指针的出现

代码示例:

int main()
{
  int* p = (int*)malloc(40);//在堆区申请40byte空间
  //判断malloc函数是否成功在堆区申请到空间,失败,返回NULL  
  //成功,返回所申请空间的初始地址
  if (p == NULL)
  {
    perror("malloc");//打印错误信息
    return 1;
  }
  //使用完毕,使用free函数对内存空间进行释放(和栈区内存的申请是不同的)
  free(p);
  p = NULL;//及时将释放空间的起始地址置于空指针
  /*free函数只能释放动态开辟的内存空间*/
  //int a = 10;
  //int* ptr = &a;
  //free(ptr);//err
  return 0;
}

3.calloc函数-->作用和malloc函数相同,进行动态内存开辟

作用:向内存申请num个size字节大小的内存空间,并把内存空间的内容初始化为0 (malloc函数并不会进行初始化)

代码示例:

//calloc函数-->void* calloc(size_t num,size_t size)
int main()
{
  //int arr[10];
  int* p = (int*)calloc(10, 4);
  if (p == NULL)
  {
    perror("calloc");
    return 1;
  }
  //打印数据
  int i = 0;
  for (i = 0; i < 10; i++)
  {
    printf("%d ", p[i]);//打印10个0
  }
  //使用完毕,释放内存空间
  free(p);
  p = NULL;
  return 0;
}

4.realloc函数-->调整动态内存大小的函数(重要,实现动态内存分配的核心函数)

作用:将ptr所指向的内存空间的大小改为size字节(增容)

但是,堆区的内存空间是有限的,在重新分配内存大小时,可能会出现内存空间不足的情况,导致返回值有三种情况;

1.返回旧空间的起始地址

2.返回新开辟空间的起始地址

3.返回NULL

当返回值不确定时,重新创建一个变量来接受

 

代码示例:

//realloc函数
//void* realloc(void* ptr ,size_t size)
int main()
{
  //动态开辟一块内存空间
  int* p = (int*)malloc(40);
  if (p == NULL)
  {
    perror("malloc");
    return 1;
  }
  //赋值
  int i = 0;
  for (i = 0; i < 10; i++)
  {
    p[i] = i + 1;
  }
  //增容
  int* ptr = (int*)realloc(p, 80);//增容为80byte
  if (ptr != NULL)//只要不是NULL,就证明开辟成功
  {
    p = ptr;
    ptr = NULL;
  }
  else
  {
    perror("realloc");
    return 1;
  }
  for (i = 0; i < 20; i++)
  {
    printf("%d ", p[i]);
  }
  //使用完毕
  free(p);
  p = NULL;
  return 0;
}

二.动态内存常见错误

1.对NULL的解引用操作(要检测返回值是否为NULL, 是NULL应该直接退出函数)

int* p = (int*)malloc(40);
  *p = 20;//未检测返回值是否为NULL
  free(p);
  p = NULL;

2.对动态开辟空间的越界访问(不能超过你申请内存空间的大小)

int* p = (int*)malloc(20);//申请了一个能容纳5个int类型数据的空间
  if (p == NULL)
  {
    perror("malloc");
    return 1;
  }
  int i = 0;
  for (i = 0; i < 10; i++)
  {
    p[i] = i + 1;//超过内存限制
  }

3. 对非动态开辟内存进行free释放(free只能释放动态开辟的内存)

int a = 10;
  int* p = &a;
  free(p);
  p = NULL;

4.使用free释放一块动态开辟内存的一部分 (不能占着茅坑不拉屎)

int* p = (int*)malloc(40);
  if (p == NULL)
  {
    perror("malloc");
    return 1;
  }
  p++;
  free(p);//ERR p此时已经不再是起始位置,不能释放内存的一部分
  p = NULL;

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

int* p = (int*)malloc(40);
  if (p == NULL)
  {
    perror("malloc");
    return 1;
  }
  free(p);
  free(p);//err  已经释放过的不能再被释放

6.动态开辟内存忘记释放(内存泄漏)

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

总结:动态内存的开辟虽然方便,但它是把双刃剑,会带来其他危险,在使用过程中要注意规范,养成以下习惯可以规避一些风险

1.在使用动态内存函数开辟完空间后,及时判断是否申请成功(p是否为NULL)

2.要及时释放动态开辟的内存(谨记malloc和free是成对出现的),并在释放后将地址置于NULL,避免野指针的出现;

3.在接受realloc函数的返回值时,最好创建一个新的变量来接收,并判断是否增容成功

补充一点

       free函数的参数中只有一个void* 类型的地址,并未告诉你对应空间的大小,那他是如何精准释放空间呢?原因在于,在malloc函数分配内存时,系统会在你所申请的内存之前(或之后,具体看编译环境)设置一个“头部信息”,free函数在得到起始地址后,会根据头部信息的内容知道内存的具体大小,从而实现对内存的精准释放

三.动态内存分配笔试题讲解

1.题目一:

解决方案:你想改变实参str的值,为实参开辟空间,改变实参,要传递实参的地址!

void GetMemory(char** p)//使用二级指针存放str的地址
{
  *p = (char*)malloc(100);//对p解引用,得到str所在的空间
}                           //为str分配足够的空间
void Test(void)
{
  char* str = NULL;
  GetMemory(&str);
  strcpy(str, "hello world");
  printf(str);//输出hello world
}

2.题目二:

注意:返回栈区空间地址,产生野指针,非法访问内存

3.题目三:

free之后一定要及时将旧地址置为空指针

四.c/c++程序内存分配

五.总结

总结:动态内存分配是一种管理内存的重要方式,要了解与动态内存管理有关的函数(malloc,calloc,realloc,free),熟记动态内存分配过程中的危险(是否分配成功,realloc函数的返回值有三种情况),了解基本的内存分配知识;感谢大家观看

目录
相关文章
|
6月前
|
编解码 测试技术 文件存储
什么是阿里云无影云手机?看完秒懂,云手机价格、创建及连接教程
阿里云无影云手机是一种基于云端的虚拟手机服务,适用于仿真测试、云游戏、数字人直播等场景。它提供轻量型、通用型、标准型和性能型四种规格,支持包年包月与按量付费模式,价格从65元/月起。使用时需创建实例组,选择地域、规格、镜像等配置,并完成支付。应用可通过控制台安装,连接方式包括管理控制台、无影客户端及ADB工具。详尽教程助您快速上手无影云手机。
644 3
|
6月前
|
机器学习/深度学习 人工智能
OmniCam:浙大联合上海交大推出多模态视频生成框架,虚拟导演打造百万级影视运镜
OmniCam是由浙江大学与上海交通大学联合研发的多模态视频生成框架,通过LLM与视频扩散模型结合实现高质量视频生成,支持文本、轨迹和图像等多种输入模态。
135 1
OmniCam:浙大联合上海交大推出多模态视频生成框架,虚拟导演打造百万级影视运镜
|
10月前
|
索引
【HarmonyOS Next开发】日历组件详细日界面组件
原生UI没有提供日历相关的组件,于是手撸了详细页面的日程。一开始打算使用list加tab的方式来实现切换的效果,但是list的切换是没有办法确定当前展示的索引的,所以没有办法实现日历内容动态添加等效果。在业内大佬的指导下,使用了两个swiper组件分别实现周和日的切换,实现了想要的效果
229 6
【HarmonyOS Next开发】日历组件详细日界面组件
|
传感器 数据采集 监控
物联网卡:如何判断并选择合适的流量套餐
要判断设备使用流量并选择合适的物联网(IoT)卡套餐,可以通过以下几个步骤来操作:
|
存储 编解码 数据可视化
FuncAnimation
FuncAnimation
|
机器学习/深度学习 数据采集 人工智能
探索软件测试中的AI辅助技术:未来趋势与挑战
【5月更文挑战第27天】 随着人工智能(AI)的迅速发展,其在软件测试领域的应用正逐步改变传统测试方法,提升测试效率和质量。本文将深入分析AI在软件测试中的应用现状,探讨其如何通过智能化的模式识别、预测分析和自适应学习机制优化测试流程。同时,文章还将讨论引入AI所面临的挑战,包括数据质量、模型泛化能力和解释性问题。最后,对未来AI辅助软件测试的潜在发展趋势进行展望。
|
网络安全 数据安全/隐私保护 Linux
【题目】2023年全国职业院校技能大赛 GZ073 网络系统管理赛项赛题第3套B模块-2
【题目】2023年全国职业院校技能大赛 GZ073 网络系统管理赛项赛题第3套B模块
|
缓存 安全 网络安全
深入理解 HTTP 和 HTTPS:提升你的网站安全性(上)
深入理解 HTTP 和 HTTPS:提升你的网站安全性(上)
深入理解 HTTP 和 HTTPS:提升你的网站安全性(上)
|
存储 数据处理 vr&ar
实时云渲染技术为何被称为VR和AR领域的加速剂?
实时云渲染技术为何被称为VR和AR领域的加速剂?
|
存储 Unix Linux
etcd集群搭建
etcd集群搭建