当前位置:首页 » 《我的小黑屋》 » 正文

C语言-动态内存管理

26 人参与  2024年11月30日 16:01  分类 : 《我的小黑屋》  评论

点击全文阅读


引言:

假设我们要写一个简单的学生成绩管理系统。在这个系统中,我们需要存储学生的成绩信息,但是我们并不知道会有多少个学生的数据需要存储。如果我们一开始就定义一个很大的数组来存储成绩,可能会浪费大量的内存空间;而如果定义的数组过小,又无法满足实际需求。那我们有什么办法来解决这种问题呢?”

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

到目前为止,我们已经有两种开辟内存的方式:

int i = 20;//在线空间上开辟四个字节char [20] = {0};//在线空间上开辟20个字节的连续空间;

不过呢!就我们目前学习的两种开辟内存的方式,都有一个共同特点:空间开辟的的大小是固定的。且对于数组来说,必须指定数组的长度,数组空间一旦确定了大小就不能调整了。

但是对于空间的需求,不仅仅会出现上诉的情况。有时候我们需要的空间大小在程序运行的时候才能知道,那数组开始就开辟的空间就有可能出现多或者少的情况。

比如引言中所提到的学生成绩管理系统,假如说我们要记录一个班级的学生,我们开辟了一个数组arr[30]的空间,但是这个班级只有十五个人,那么就会有15个空间被浪费;如果该班级有31个人,那么我们开辟的空间又会不够,无法存放该班级所以学生的信息,这时候就会发现已知的两种开辟空间方式的局限性。

所以针对这种情况C语言引入了动态内存开辟,让程序员自己可以申请和释放空间。增加了灵活性。

动态内存分配的特点:

主动申请大小调整释放空间

2、malloc和free

2.1 malloc函数

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

void* malloc (size_t size);//函数原型

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

如果开辟成功,返回一个指向该空间的指针。如果开辟失败,则返回一个空指针NULL。返回值的类型是void*是因为malloc函数不知道开辟空间的类型,具体在使用的时候由使用者自己来决定如果参数size为0时,malloc的行为时标准还是未定义的,取决于编译器。

参数介绍:size_t size:

ff8645377b044346a5f3efc1d3580e2c.png

size是待开辟的内存块大小,注意是以字节为单位的。

返回值介绍:void*:
cc6bdeb8084a4385994de9151a3fcfc5.png

示例:

#include <stdio.h>int main(){//比方说要存放十个整型int* p = (int*)malloc(40);//10*sizeof(int)//使用空间int i = 0;for (i = 0; i < 10; i++){*(p + i) = i + 1;}for (i = 0; i < 10; i++){printf("%d ", *(p + i));}}

如果你需要存放整型,就使用整型指针来接收,然后将malloc类型从void*强制转换为int*。

当我们开辟完空间使用后,我们不打算继续使用它,便需要将空间释放,这时候就需要用到另一个函数了。这就相当于你去图书馆借书,当你看完后,你需要将书在还回去,这样别人才能够借到这本书。空间亦是如此。

2.2 free函数

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

void free (void* ptr);

注意:free函数只能用来释放动态开辟的内存

如果参数ptr指向的空间不是动态开辟的,那free函数的行为是未定义的。如果参数ptr是NULL指针,则函数什么都不用做。

malloc和free都声明在stdlib.h头文件中。

函数原型解释:

这里的void* ptr一定指的是你申请到的那块动态内存的起始地址。不能是你要释放的那块空间的中间地址,只能是起始地址。

示例:

#include <stdio.h>int main(){    //开辟空间int* p = (int*)malloc(40);//使用空间int i = 0;for (i = 0; i < 10; i++){*(p + i) = i + 1;}for (i = 0; i < 10; i++){printf("%d ", *(p + i));}//释放空间free(p);    p = NULL;return 0;}

注意这里free接收的指针p,它的地址是没有变化的,从始至终都是我们开辟的动态内存的起始地址。因此,我们将p传给free函数用来释放内存。
22f3b68dd5d846479ed2d2ab8dcbf17b.png

free的作用呢,便是断开上面的箭头,我们不能在使用上面的那块空间了。

但是呢,free断开后,p里面还是会记录着开辟空间的起始地址。

这样的话,你通过p里面的地址,还能找到已经还给操作系统的空间了。因此为了防止这种情况发生,我们可以给p赋值一个空指针。p = NULL;

这里有一个比较生动的解释,可能有些扎心。

比如说你交了一个女朋友,后来你们两分手了,但是你还记着人家的电话号码,你依旧可以通过这个电话号码找到你前女友,并天天给人家打电话,这样合适吗?肯定不合适,这时候需要给你重重一击,让你彻底忘记你前女友的电话号码,彻底断开你们之间的联系。

这里给p赋上空指针还是很有必要的,毕竟,如果不赋上空指针,p里面存的地址就变成了野指针了。

明明我指向那块空间,但是那块空间却不属于我,明明喜欢那个女孩,但是那个女孩却不喜欢我。这就是如果不给free赋上空指针的结果。

关于野指针的危害前面也有介绍,这里就不细说了。

关于free,free只是将这块空间还给操作系统,但是p里面的地址还是原来的地址。所以切记,在使用完free后,要记得给p赋上NULL。


如果在开辟完动态内存后不free的话会怎么样,这样的话你就会一直占用着这块空间,就像你去图书馆借书,你既不还书,也不毕业,这样的话这本书就会一直在你手上,别人想借这本书就借不到。

但是如果你一直不换书的话,最后等你毕业后,学校也会强制收回你借的那本书。也就是说,你一直不free的话,等到程序结束后,操作系统也会主动的将你开辟的动态内存空间回收。

3、calloc和realloc

3.1 calloc函数

在C语言中还提供了一个函数calloc,calloc函数也用来动态内存分配,函数原型如下

void* calloc(size_t num,size_t size);

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

与函数malloc的区别只在于calloc会在返回地址之前把申请到的空间的每个字节初始化为全0.

#include <stdio.h>int main(){    //开辟    int* p = (int*)calloc(10,sizeof(int));    //使用         //释放    free(p);    p = NULL;    return 0;}

如果我们要对申请的内存空间的内容要求初始化,那么可以使用calloc函数来完成。

3.2 realloc函数

realloc函数的出现使得动态内存管理更加灵活。

有时候我们会发现申请的空间太小了,有时候会觉得申请的空间过大了,所以为了能够合理的使用内存,我们需要进行对内存大小的灵活调整。因此realloc函数就横空出世了!

函数原型如下:

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

解读:

ptr是要调整的内存地址size是调整之后新的空间大小返回值为调整之后的内存起始位置。这个函数调整原内容空间大小的基础上,还会将原来内存中的数据移动到新的空间。realloc在调整内存空间的时候存在两种情况            1.原有空间之后有足够大的空间            2.原有空间之后没有足够大的空间

示例:

#include <stdio.h>#include <stdlib.h>int main(){int*p = (int*)malloc(20);//5个整型//使用int i = 0;for (i = 0; i < 5; i++){*(p + i) = i + 1;}//1 2 3 4 5//在扩大p的20个字节空间relloc(p, 40);//在原基础上扩大20个字节,因此新空间占40个字节return 0;}

realloc的两种开辟空间的情况。
673d3f357e824132a765e3d3564b2e7b.png

如果原来空间有足够的地方开辟要扩展的空间,那么realloc就在原空间接着开辟。如果原来空间不够开辟要扩展的空间,那么realloc就会在内存中新找一块大小合适的空间俩开辟,并会将就空间的数据拷贝到新空间内,最后释放旧的空间,并返回新空间的地址

因此,关于realloc的返回值,我们是否还能拿p来接收?在上面的示例中,答案肯定是不行的

如果开辟失败的话,会给p赋上空指针,导致原来开辟的空间也找不到了。

因此这里我们需要单独创建一个指针变量ptr

int* ptr = (int*)relloc(p, 40);//在原基础上扩大20个字节,因此新空间占40个字节if (ptr != NULL){p = ptr;}

如果ptr这个指针不是空指针,那么我们就将ptr中的值赋给p。

如果是情况1的话,ptr的值等于p,所以赋值等于没赋;如果时情况2的话,p指向的那块旧的地址释放掉了,因此我们将ptr的地址(也就是新开辟的空间的起始地址)给到p。


结语:

本篇文章介绍了动态内存所需要的一些函数,下篇文章将会介绍常见的动态内存错误问题

 

 


点击全文阅读


本文链接:http://zhangshiyu.com/post/194542.html

<< 上一篇 下一篇 >>

  • 评论(0)
  • 赞助本站

◎欢迎参与讨论,请在这里发表您的看法、交流您的观点。

最新文章

  • 林晚夏江肆年(进错房,嫁给八零最牛特种兵在线阅读)全文免费阅读无弹窗大结局_(林晚夏江肆年)进错房,嫁给八零最牛特种兵在线阅读免费阅读全文最新章节列表_笔趣阁(林晚夏江肆年) -
  • 进错房,嫁给八零最牛特种兵完整版阅读小说(林晚夏江肆年)全文免费阅读无弹窗大结局_(进错房,嫁给八零最牛特种兵完整版阅读)林晚夏江肆年免费阅读全文最新章节列表_笔趣阁(进错房,嫁给八零最牛特种兵完整版阅读) -
  • 新雪藏旧事全文全文(商云萝周砚京)全文免费阅读无弹窗大结局_(新雪藏旧事全文小说免费阅读)最新章节列表_笔趣阁(新雪藏旧事全文) -
  • 在线免费小说重生七零替嫁:不嫁教授,嫁军官_乔珊珊乔婉月新热门小说_热门小说乔珊珊乔婉月
  • 免费小说《冯云漪厉晋泽》已完结(冯云漪厉晋泽)热门小说大结局全文阅读笔趣阁
  • 祁兰湘邵黎晖小说_祁兰湘邵黎晖完整版大结局小说免费阅读
  • 完整免费小说老公心疼青梅将她留宿新房,却将怀孕的我赶出家门(乔玥傅慎行姜禾)_老公心疼青梅将她留宿新房,却将怀孕的我赶出家门(乔玥傅慎行姜禾)完本小说免费阅读(乔玥傅慎行姜禾)
  • 新雪藏旧事:结局+番外+完结免费小说在线阅读_小说完结推荐新雪藏旧事:结局+番外+完结商云萝周砚京热门小说
  • 初逢青山梦长安(顾怀瑾沈书妤)阅读 -
  • 无删减版《绝对权力:从天崩开局走上官途巅峰》在线免费阅读
  • 《绝对权力:从天崩开局走上官途巅峰》小说在线试读,《绝对权力:从天崩开局走上官途巅峰》最新章节目录
  • 裴泽苏星辰何娇(满目星辰不及你小说)精彩章节在线阅读

    关于我们 | 我要投稿 | 免责申明

    Copyright © 2020-2022 ZhangShiYu.com Rights Reserved.豫ICP备2022013469号-1