当前位置: 首页 > news >正文

C语言学习笔记——动态内存分配

目录

  • 1 动态内存分配常用库函数
    • 1.1 free
    • 1.2 malloc
    • 1.3 calloc
    • 1.4 realloc
  • 2 常见的动态内存错误
    • 2.1 对NULL指针的使用
    • 2.2 越界访问
    • 2.3 对非动态开辟的空间进行释放
    • 2.4 对动态开辟的空间重复释放
    • 2.5 释放部分动态开辟的空间
    • 2.6 忘记释放动态开辟的内存空间
  • 3 柔性数组

1 动态内存分配常用库函数

1.1 free

free 的函数声明如下:

voidfree(void*ptr);

作用
将动态开辟的空间进行回收

参数部分
指向动态分配内存空间的指针,若该指针为空指针,则 free 不会做任何事情

返回值
该函数没有返回值

一般来说,在使用了 malloc 等函数开辟动态内存空间后,若这块空间不需要使用,就要用 free 函数来进行回收,并将指针置为空,防止内存泄露和野指针问题

1.2 malloc

malloc 的函数声明如下:

void*malloc(size_tsize);

作用
向操作系统申请 size 个字节大小的连续内存空间

参数部分
要分配的空间大小,以字节为单位

返回值
如果空间开辟成功,会返回这块连续内存空间的首地址,用户可以根据自己的需要,将返回的 void* 类型的指针进行强制类型转换,但是如果开辟失败,会返回空指针,所以用户应该在使用 malloc 后,检查指针的值是否为空,防止对空指针进行使用

举例:向操作系统申请 20 个字节大小的空间用于存储 int 类型数据

#include<stdlib.h>intmain(){int*p=(int*)malloc(sizeof(int)*5);if(p==NULL)//防止对空指针进行使用{perror("malloc");return1;}free(p);//回收开辟的空间p=NULL;//防止野指针return0;}

int 类型大小 4 个字节,乘以 5 就是 20 个字节,由于要存放 int 类型数据,所以返回的指针强制类型转换为了 int*

申请了空间就需要使用,那么怎么使用这块空间呢?
在使用动态开辟的内存空间时,可以当成数组来使用,通过数组的遍历方式来使用这块空间

举例:开辟20个字节的空间存放 1 2 3 4 5 并输出

#include<stdlib.h>intmain(){int*p=(int*)malloc(sizeof(int)*5);if(p==NULL){perror("malloc");return1;}for(inti=0;i<5;++i){*(p+i)=i+1;//*(p + i) = p[i]}for(inti=0;i<5;++i){printf("%d ",*(p+i));}free(p);p=NULL;return0;}

图解:

1.3 calloc

callo 的函数声明如下:

void*calloc(size_tnum,size_tsize);

作用
开辟 num 个大小为 size 的空间,并初始化为全 0

参数部分
num指元素个数
size指一个元素的大小

返回值
开辟成功返回指向动态开辟空间的指针,开辟失败则返回空指针

举例:开辟20个字节的空间存放 1 2 3 4 5 并输出

intmain(){int*p=(int*)calloc(5,sizeof(int));//开辟5个大小为4个字节的空间if(p==NULL){perror("malloc");return1;}for(inti=0;i<5;++i){*(p+i)=i+1;//*(p + i) = p[i]}for(inti=0;i<5;++i){printf("%d ",*(p+i));}free(p);p=NULL;return0;}

1.4 realloc

realloc 的函数声明如下:

void*realloc(void*ptr,size_tsize);

作用
如果 ptr 为空,则开辟 size 个大小的空间,单位是字节
如果 ptr 不为空,则将 ptr 所指向的空间扩张至 size 个大小

参数部分
ptr 为指向动态开辟空间的指针
size 为新空间大小

返回值
开辟成功返回空间首地址的指针,开辟失败返回空指针

realloc 在重新分配空间时使用的方法
第一种:原空间后方的空间余量够扩大到 size 个时,则直接向后扩张即可
比如说,现在已经动态分配了 20B 的内存空间,想要扩张至 40B,在已分配空间后方还有 20B,够扩张到 40B,那么,就会直接向后扩张
图解:

第二种:原空间后方的空间余量不够扩大到 size 个时,则需要另找一片空间,重新分配
比如,现在已经动态分配了 20B 的内存空间,想要扩张至 40B,但是已分配空间后方不够 20B,不够扩张到 40B,那么,就会寻找一片 40B 的空间进行分配,将原空间的数据复制到里面,返回指向新空间的指针

举例:开辟20个字节的空间存放 1 2 3 4 5 后,将其扩大到 40B,再存放 6 7 8 9 10 并输出

#include<stdlib.h>#include<stdio.h>intmain(){int*p=(int*)malloc(sizeof(int)*5);if(p==NULL){perror("malloc");return1;}for(inti=0;i<5;++i)//存放1 2 3 4 5{*(p+i)=i+1;//*(p + i) = p[i]}int*p2=(int*)realloc(p,40);if(p2==NULL)//检查新空间是否开辟成功{perror("realloc");return1;}p=p2;//开辟成功再改变指针指向p2=NULL;for(inti=5;i<10;++i){*(p+i)=i+1;}for(inti=0;i<10;++i){printf("%d ",*(p+i));}free(p);p=NULL;return0;}

2 常见的动态内存错误

2.1 对NULL指针的使用

intmain(){int*p=(int*)malloc(INT_MAX*2);*p=10;//p若为NULL,不能解引用free(p);p=NULL;return0;}

为了防止这个问题,需要对指针进行检查

intmain(){int*p=(int*)malloc(INT_MAX*2);if(p==NULL)//防止空指针问题{perror("malloc");return1;}*p=10;//p若为NULL,不能解引用free(p);p=NULL;return0;}

2.2 越界访问

intmain(){int*p=(int*)malloc(sizeof(int)*5);if(p==NULL){perror("malloc");return1;}for(inti=0;i<=5;++i)//只有5个元素,但是下标遍历到了6{*(p+i)=i;}free(p);p=NULL;return0;}

2.3 对非动态开辟的空间进行释放

intmain(){intarr[]={1,2,3,4,5};free(arr);//arr不是动态开辟的空间return0;}

2.4 对动态开辟的空间重复释放

intmain(){int*p=(int*)malloc(sizeof(int)*5);if(p==NULL){perror("malloc");return1;}free(p);free(p);p=NULL;return0;}

为了防止这个问题,应该在第一次释放后,将指针置为空

intmain(){int*p=(int*)malloc(sizeof(int)*5);if(p==NULL){perror("malloc");return1;}free(p);p=NULL;free(p);return0;}

2.5 释放部分动态开辟的空间

intmain(){int*p=(int*)malloc(sizeof(int)*5);if(p==NULL){perror("malloc");return1;}p++;free(p);p=NULL;return0;}

2.6 忘记释放动态开辟的内存空间

intmain(){int*p=(int*)malloc(sizeof(int)*5);if(p==NULL){perror("malloc");return1;}return0;}

3 柔性数组

柔性数组是通过结构体来实现的,具体实现方法为:

  • 将结构体的最后一个成员设置为数组,该数组前一定要有一个或多个成员变量
  • 此数组不设大小
structS{inta;intarr[];//柔性数组成员};

当结构体内有柔性数组,使用 sizeof 在计算类型大小时,不会将柔性数组的大小计算入内,只会计算柔性数组之前成员的大小,因此就可以使用动态分配的方式指定数组的空间大小

举例:分配大小为 20B 的空间给柔性数组

structS{inta;intarr[];//柔性数组成员};intmain(){structS*p=(int*)malloc(sizeof(structS)+sizeof(int)*5);//先分配柔性数组前成员变量的空间,再分配柔性数组的空间if(p==NULL){perror("malloc");return1;}free(p);p=NULL;return0;}

图解:

如果想要更改柔性数组的大小,只需要使用 realloc 即可

举例:将 20B 的柔性数组变成 40B

structS{inta;intarr[];//柔性数组成员};intmain(){structS*p=(int*)malloc(sizeof(structS)+sizeof(int)*5);if(p==NULL){perror("malloc");return1;}structS*p2=(int*)realloc(p,sizeof(structS)+sizeof(int)*10);//更改为40Bif(p2==NULL){perror("malloc");return1;}p=p2;p2=NULL;free(p);p=NULL;return0;}

图解:
假设后方有空间

http://www.cnnetsun.cn/news/3402823.html

相关文章:

  • 无人机视角航拍河道巡检垃圾及周边坍塌隐患检测数据集VOC+YOLO格式6686张6类别
  • Vue3中computed详解
  • 048、手机影像调优实战:夜景模式、人像虚化与多摄融合的调试经验
  • 技术解析:ChanlunX缠论分析插件在通达信中的实现路径与架构设计
  • CANN/asc-devkit块内同步指令
  • openEuler内核故障排查终极指南:10个常见问题与解决方案大全
  • Loop:5分钟掌握Mac窗口管理终极技巧,告别桌面混乱
  • 从理论到实战:深度解析白盒测试七大覆盖准则及其在代码审查中的应用
  • 如何在Linux系统上轻松安装专业版Photoshop CC 2022:完整指南
  • 从卡文到爆更自由:ChatGPT辅助小说写作全流程拆解,含大纲生成→伏笔回收→风格克隆→版权合规四重关卡
  • CANN Ascend C GetAccVal API文档
  • 收藏!小白程序员轻松入门大模型,6大行业真实案例带你玩转Agent系统!
  • IGBT(绝缘栅双极型晶体管)简介
  • AirSim源码编译
  • 从界面到智能体:前端工程师的Agent开发进阶之路(收藏版)
  • 终极免费教程:5步让老旧Mac完美运行最新macOS系统
  • 技术选型指南:tiny11maker vs tiny11coremaker Windows 11镜像精简技术深度对比与最佳实践
  • Luanti模组开发进阶:从入门到精通的完整教程
  • CANN/asc-devkit SIMD-API数据搬运填充值设置
  • 知识体系即护城河:ChatGPT赋能下的个人IP底层架构(含2024最新RAG+Graph RAG双轨构建协议)
  • Laravel Developer Roadmap:数据库设计与Eloquent ORM实战指南
  • 为什么openeuler/peking_university_2021仓库被关闭?官方说明与社区影响分析
  • 【VScode】无法建立ssh连接问题
  • 遥感、GIS和GPS技术在水文、气象、灾害、生态、环境及卫生等领域中的实践应用
  • CANN/asc-devkit TPipe销毁无管道同步API
  • OpenCode+Copilot:国产化AI编程工作流实战指南
  • Postman便携版终极指南:告别安装烦恼,拥抱绿色API测试新体验
  • 从混沌到闭环:ChatGPT驱动的个性化学习路径构建法,含神经科学验证的4阶段记忆强化模型
  • 终极指南:如何在Windows上轻松安装安卓应用?5步掌握APK安装器
  • Ascend C SIMD-API Layout函数