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

C语言动态内存管理

        上期我们手搓出了一个通讯录,当时是创建了一个名为context的结构体,里面又有装着人的信息的数组peo_info,我们创建了100个这个类型的数组,因此内存是非常大的,如果我们只在里面存了5~6个人的信息那岂不是大材小用??所以此时就有一个思路,我们把初始时候的大小设为5个,满了就+2个,以此类推,那么空间就不会被浪费太多对吧,所以C语言中动态内存的知识就来咯~

一、动态内存函数介绍

        1.1 malloc和free

        C语言提供了动态内存开辟的函数malloc

void* malloc(size_t size)//动态内存的开辟

        这个函数会向内存申请一个 连续可用的空间,并返回空间的起始地址(指针)

        另外还需要注意以下4点:

        1. 如果开辟空间成功,则返回空间的指针

        2. 如果开辟空间失败,则返回空指针,因此开辟完空间的时候需要我们去检查一下这个指针是否为空

        3. 返回类型为void*,因为他并不知道我们要开辟的这块空间的类型是什么样的,所以我们在接收指针的时候需要强制类型转换一下

        4. 当size = 0的时候,纯纯犯贱,此时连系统都看不下去懒得救你

         来举个例子,假设我们需要创建大小为num的int型数组,我们可以这样写:

	int num = 0;scanf("%d", &num);int arr[num] = { 0 };

        但是这样写在VS的环境下是不行滴,所以我们就需要改为动态内存

	int num = 0;scanf("%d", &num);int* ptr = (int*)malloc(sizeof(int) * num);//动态内存开辟if (ptr == NULL)//检测是否开辟成功{perror("malloc:");return 1;}

        之后就可以进行对应的操作啦,当你操作完了之后,要记得向内存还回去这个空间,不要有借无还噢,因此我们就需要用到空间释放的函数free

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

void free (void* ptr);//空间的释放

        需要注意的是如果ptr为空指针那么函数什么都不干,空间释放完了之后他不会把ptr置为空指针,因此为了防止ptr成为野指针,需要我们自己将ptr置为空指针

	int num = 0;scanf("%d", &num);int* ptr = (int*)malloc(sizeof(int) * num);//动态内存开辟if (ptr == NULL)//检测是否开辟成功{perror("malloc:");return 1;}//操作//...//释放free(ptr);ptr = NULL;

        这样才正确使用了动态内存的开辟和释放

        1.2 calloc

        calloc和malloc类似,都是动态内存开辟的函数

void* calloc (size_t num, size_t size);

        不一样的是calloc需要我们输入两个参数,第一个是元素的个数,第二个是元素的大小,开辟之后的空间它会全部帮我们初始化为0

int main()
{int num = 0;scanf("%d", &num);int* ptr = (int*)calloc(num, sizeof(int));//动态内存开辟if (ptr == NULL)//检测是否开辟成功{perror("calloc:");return 1;}//操作//...//释放free(ptr);ptr = NULL;return 0;
}

        malloc是不会帮你初始化这块空间的哟,所以我个人认为calloc更方便一些

        1.3 realloc

        动态内存的动来了,realloc函数的出现让动态内存管理更加灵活,它可以使我们申请的空间大小变为我们想要的大小

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

        显然第一个参数是开辟空间的指针,第二个是改变后的空间大小(字节)

        需要注意的是当他改变空间成功之后还是返回这个空间的指针,但是如果没有开辟成功的话返回的是一个空指针(NULL),因此我们应该先检测一下返回的指针是否为空指针,然后再造作,以防万一找不到之前开辟的空间

         realloc调整空间有两种方式;

        1.  如果后面有足够多的连续空间,那么可以直接将需要增加的空间放后面(连续)

        2. 如果后面连续的空间不够,函数会在堆区找到合适大小的空间,返回新地点的指针

int main()
{int* ptr = malloc(100);if (ptr == NULL){perror("malloc:");return 1;}//操作//……//增加空间int* p = realloc(ptr, 200);if (p)//先检测p是否为空指针,以防空间丢失{ptr = p;}else{perror("realloc:");return 1;}free(ptr);//释放空间return 0;
}

二、通讯录2.0版(动态内存版)

        有了上述的知识之后,我们就可以将我们的通讯录弄得更为完善

        2.1 结构体context的修改

        这是我们上一个静态版本的context结构体

//静态版本
typedef struct context
{peo_info data[MAX];//存放人的信息int sz;//存放当前存的人的个数
}context;

        我们用的是一个100大小的数组来控制我们的通讯录的大小,我们要将它改为动态内存版的就不需要数组了,只需要一个指针(指向开辟好的内存空间)就行,然后我们还需要一个值来纪律当前的内存大小是多少,方便之后增加内存

//动态版本
typedef struct context
{peo_info* data;//存放人的信息int sz;//存放当前存的人的个数int capacity;//当前通讯录的最大容量
}context;

        2.2 初始化函数的修改

        这是我们上个版本的初始化函数

//初始化通讯录
void init_context(context* pc)
{assert(pc);pc->sz = 0;memset(pc->data, 0, sizeof(pc->data));
}

        现在我们要先申请5个context字节大小的空间,并初始化,所以直接用calloc函数,申请并初始化空间,再把当前申请好的空间初始化为5

void init_context(context* pc)
{assert(pc);pc->sz = 0;peo_info* ptr = (peo_info*)calloc(DEFAULE_SZ,sizeof(peo_info));if (ptr == NULL){perror("init_context::calloc");return;}pc->data = ptr;pc->capacity = DEFAULE_SZ;
}

        2.3 增加空间

        每次增加联系人的时候都检查一下空间满了没有,如果满了,就像内存再申请2个空间

//动态版本
void check_capacity(context* pc)
{assert(pc);if (pc->sz == pc->capacity){//增加容量peo_info* ptr = realloc(pc->data, (pc->capacity + INC_SZ) * sizeof(peo_info));if (NULL == ptr){perror("check_capacity::realloc");return;}pc->data = ptr;pc->capacity += INC_SZ;printf("增加容量成功\n");}
}void add_context(context* pc)
{assert(pc);check_capacity(pc);//增加一个人的信息printf("请输入名字:");scanf("%s", pc->data[pc->sz].name);printf("请输入年龄:");scanf("%d", &pc->data[pc->sz].age);printf("请输入性别:");scanf("%s", pc->data[pc->sz].sex);printf("请输入电话:");scanf("%s", pc->data[pc->sz].tele);printf("请输入地址:");scanf("%s", pc->data[pc->sz].addr);pc->sz++;printf("添加成功\n");
}

        这样我们就能得到一个内存空间灵活的通讯录

三、动态内存常见的错误

        3.1 对NULL解引用

int main()
{int* ptr = malloc(100);*ptr = 20;//如果没有开辟成功,ptr就为空指针free(ptr);return 0;
}

        因此我们需要先检查malloc是否返回的是空指针

int main()
{int* ptr = malloc(100);if (ptr == NULL){perror("malloc:");return 1;}*ptr = 20;//如果没有开辟成功,ptr就为空指针free(ptr);return 0;
}

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

int main()
{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);return 0;
}

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

int main()
{int a = 10;int* p = &a;free(p);//这样可以吗?return 0;
}

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

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

        3.5 重复释放

int main()
{int* p = (int*)malloc(100);free(p);free(p);//多次释放
}

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

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


http://www.mrgr.cn/news/54483.html

相关文章:

  • 基于微博评论的自然语言处理情感分析
  • Vue2、Vue3温习解惑知识点
  • 极氪汽车困局:营销频繁车、产品力不足
  • C++题集
  • 决策树:从理论到实践
  • Java网络编程 - headers
  • linux线程 | 同步与互斥 | 深度学习与理解同步
  • 【大数据分析与挖掘算法】matlab实现——DBSCAN聚类方法
  • Git_IDEA集成Git
  • “敌人野猪”受伤和死亡的逻辑和动画
  • xtu oj 不定方程的正整数解
  • yjs机器学习数据操作01——数据的获取、可视化
  • 民宿预订新纪元:SpringBoot实现的在线平台
  • 昇思MindSpore进阶教程--AOE调优工具
  • 大幅降低人工核验遗漏的概率,降低出错风险的智慧能源开源了
  • QT的事件
  • SpringBoot技术在汽车票预订领域的应用
  • 状态空间表达式的求解与转化【现代控制理论】
  • 第6天:Intent和页面导航
  • 管家婆财贸ERP BB007.销售订单明细批量采购
  • 大数据治理--技术平台与工具
  • 深入探索 APKTool:Android 应用的反编译与重打包工具
  • 2024软考网络工程师笔记 - 第12章.网络规划设计
  • 【软件运行类文档】项目试运行方案,试运行计划书(word原件)
  • Windows环境下安装jdk8,含配置环境变量全过程
  • 基于MATLAB车牌识别系统设计