00:01
同学们,我们来看一下动态内存分配的这个知识,那么在C语言中呢,不同的数据是在内,在这个内存中是有不同的分配的。大家还记不记得我们已经讲过站。也讲过,在静态存储区是不是可以存放我们的这个静态数据呀。还有一样东西,就是大家有没有发现这个堆,我们一直没有讲过什么情况下我们的数据会放在这个堆里面呢?现在我们就来说一说。大家可以看到全局变量。类,它全局变量会存放在我们的这个静态存储区,也就是全局区放这。非静态的局部变量会放在战区,对不对?这个战区,那么还有一种就是现在我们说的堆区临时使用的数据,比如说我们临时需要使用一些数据,那么建立动态内存分配区需要随时开辟。
01:03
不需要的时候呢,咱们就及时的释放,这就对,也就是说在我们这个程序开发中,假如我们需要一块内存。我们需要动态的开。因为动态的开放,就是当我们程序执行到这个时候的呢,他在给我们开辟的时候,我们就可以在这个堆里面通过相关的方法来开一块内存,然后使用,使用完了过后呢,要我们程序员自己及时释放,释放这个动作要程序员自己来完成。那么第四点根据需要向系统申请所需要的大小空间。由于未未未能声明。呃,由于未在声明部分定义起变量或者数组,不能通过变量名或者数组名来引用这些数据,那怎么办呢?我们需要指针,也就是说你在这个堆里面分分配了一块内存,通常情况下是让这个一个指针指向这块内存,然后通过这个指针来访问这个堆里面的数据。明白这个意思了吧?好,现在呢,我们来看看。
02:09
在这个动态内存分配里面有哪些相关的方法,我们来说一说,待会举个例子就可以了。首先说第一句话,我们这个内存动态分配的相关函数呢,它是放在standard lab这个头文件里面的,重点有这么四个,一个是。嗯,啊,MYMY这个这个单词是这样理解的。它的一个全称是memory。Memory or location?这个地方大家能知道什么,就是内存动态分配的意思,内存分配的意思,所以说他取的是前面第一个M和这个alloc里前面的四个字母组成的。你看。是不是,哎,这个还有一个C5个字母,也就是说是由前面的M再配上后面的alloc的前五个字母构成的这个函数名。
03:03
这个是函数,那么这个函数它到底是来做什么事情的呢?我们直接给大家看案例,它的作用是在内存动态存储区,也就是这个堆区。我们所说的动态存储区。就是我们前面讲的那个堆区。能能理解堆堆曲。在这里面分配一个长度为S值的连续空间,注意是连续的空间啊,呃,然后它的参数呢,是一个无符号的整数,对,那么我们举个例子。啊,我们举举个例子,比如说我写了一个my local,然后100,它会在我们的这个堆区里面呢,开辟一个100个自己的临时空间,并且把这个返回值的第一个地字节的地址返回。啊,比待会儿我们再举例,就比如说这是我们的堆,它会在这里面呢,开辟一个100个字节大小的空间,然后把这个第一个字节的地址给你返回来,好,就这个意思明白了吧,第二个。
04:06
这个叫c local c local呢,它相对来说,它是用来去分配这个。呃,较大的,分配的空间相对比较大,呃,一般是用来分配给一个数组的,我直接举个例子,大家一看就明白了,比如说。我给大家举个例子,比如说现在有个C54。这表示什么意思呢?表示分配一个50乘以四的字节的空间,然后把这个空间的首地址。返回给这个P啊,它是这样子的,它是50乘以四,往往是用来给数组分配,为什么呢?因为因为一般来讲,我们比如说要分配给四个数组,对吧,后面我们可以写个四,假设我们这个数组呢,每个数组有十个,十个元素就是十乘以四,这样好算,但是本质跟my local啊,其实是差不了太多的,都是分配连续的空间,只是这种方式呢,一般分配的空间较大。
05:05
好,我们再来看下一个函数,叫free,很简单,F的作用呢,它就是释放变量,这个P就是我们这个指针啊,这个指针它所指向的动态空间,比如说在我们这个堆里边,就是我们的堆里面有有有一块空间,有这么大块空间啊。原先呢,是有一个指针指向这个这个空间的,但是这个空间呢,因为种种原因我不想用了,怎么办呢?我来一个free,把这个P放进去就行了,就说free。P,如果这个动作一操作呢,那么堆这个堆区的这个空间就会被回收。明白这意思吧,就是说别人就可以用了,这就是free的作用,还有一个,还有一个叫做呃,Re a local,这个是干什么的呢?这个呀,它是这样子的,重新分配my local和c local函数获取的动态空间大小,将P指向动态空间大小改为S。
06:08
然后呢,P的值不变,分配失败,返回一个空,这句话呢,我给大家举个例子,大家就明白了,还是画一个图啊,注意这些函数都比较简单,我直接画就行了,比方说先前呢,我们有一个P指向堆里面的一个空间。好,这个空间原先是多大呢?比如说是123455个自己,假如哈是五个自己。假如是五个自己,但是现在呢,我有一个需求,我的需求是,呃,这个空间我想缩小一点或者扩大一点。啊,可能是做大,可能是扩大,可能是缩小,无所谓,然后呢,如果我这样写了,写了一个RA local p50,那么你原先是五个,它就会在后面再给你加更多的空间,但是这个P呢,仍然是指向我们原先分配的这个空间的第一个字节的地址,就这个意思叫re a local,比如说你是五个啊,现在就变大了,后面加就行了。
07:11
对,看这写的姜P。呃,将P所指向的已分配的动态空间改为50个字节。啊,就这个意思,如果你缩小了呢,相当于说把这块给你去掉。然后呢,缩小到只有,比如说我把这个改成三,那变成什么呢?只留下了三个字节啊,这个就变成三了,后面的两个字节呢,就不要了。就相当于释放出去,好,这就是re local,那明白这个东西呢,我们再来看一个,这边有一个东西,大家有没有发现有个叫做VO的心。VO的心,这个褒的心是什么呢?我们来看一下它是这个。在C99标准中。呃,标准以上把这个my local c local re local的函数的积类型定义成voe的类型,这种指针称为无类型指针。
08:05
G它不指向哪一种具体的这个类型数据,只表示用来指向一个抽象的数据,G仅仅是一个纯地址,再说一遍啊,它是一个纯地址,而不能指向任何具体的对象或者叫数据。也就是说,假如我们有个VO的这样的一个指针,那么这个指针呢,它就是指向一个地址,它就是这边存了一个地址而已,但是这个地址呢,并不指向任何的一个数据,明白了吧,这条线是没有的。他就是存了一个例子。它就是一个纯地子,明白了吗?就再说一遍啊,VO的心,它是一个纯地子,并不指向任何具体的对象,或者说说变量,明白这个意思了哈,明白这个,那下面老师继续来看贸易的指针呢,这边有几个案例,我们来看一看。
09:02
好,我们来看几个吧,这个定一个A,这个是把A地址付给P1没问题,这个是P2指向char类型的变量P3,看这里P3为无类型指针变量。机类型为贸易的类型。啊,也就是说P3它不,它是一个指针,确实是一个指针,但是它不指向任何的变量,明白这个意思吗?好,大家看我这里写了一个P1贸易的新,就是把P1。把P1这个类型转成贸易的心付给P3,但是我要再说一遍啊,P本身并没有变化。P1本身这个还是指向一个整数的,只是呢,他把这个地址拿过来变成了贸易的星付给了P3,然后呢,这有个又把P3转成了恰新给了P2,也就是说我们这个P3呢,因为它这个P3现在不是贸易的吗。
10:02
啊,是个贸易的心,那么我可以把这个转成一个差星付给P2。好,这个地方是OK的,这个是合法输出A的值,因为P1指向这个A的一个地址,所以说用星号P可以,但是这样用是不行的。这种用法不行,大家知道为什么不行吗?你新P3是不可以的,我们刚才讲过P3呢。它是一个什么类型?它是一个VO的voe的星,它并不指向任何一个具体的数据,因此你就不能用星号P3,这样用是直接报错的。再说一遍,就是大家记住一个结论,什么结论呢?如果是一个VO的指针类型,你不可以用星号P的方式来取得这个P指向的一个变量值,不可以,因为它本身就是一个纯地址。明白了哈,好明白了过后呢,我们再来看下面这句话,哎,下面这句话,下面这句话大家可以看到,呃,我把一个呃在很多情况下呢,贸易的指针,呃当把一个贸易的指针赋赋值给不同的机类型的指针变量时,或相反系编译系统会自动的转换,比如说大家看。
11:18
这个地方呢,A本身。A本身是一个in星,对吧?那么这个in特星呢?我直接把这个特星的地址,呃呃,不是A是一个整数,A是一个整数,看一下A对A是一个整数,AA是一个整数,我把这个A的地址付给这个P是可以的,因为这P3等于一个艾特符的A,其实就是把A的地址给到P3,但本身来说这个应该是不可以的。为什么不可以呢?因为你的类型是int型。而它的类型是VO的星,是这个意思吧,本身不匹配是不是一个是特星,一个是VO星,当然不匹配了,但是呢,我们这讲过,现在有很多版本,就是像C99这个版本,编译器这个版本,它会怎么呢?它会它会自动的进行一个转换,也就是说你这样写。
12:16
等价于这种写法。明白了吧,但是这个呢,有一个要求是C99的编译器才支持的,如果是低版本的呢,并不支持,还得还得呃,还得自己强转一下,还得写成下面这个才行明白,也就是说如果是C99这个版本,你这样直接复制是OK的,但是如果是没有这个C99这个版本这么高,你前面在这个地方,你仍然要加上一个什么呢,VO的。心这个强制转换,好吧,这个就说到这儿,那现在呢,我们说这么多,我们举个例子吧,我们举个例子,大家一下就知道前面几个方法到底是怎么用的来,现在呢,我们动态的创建一个数组,输入五个学生的乘积,然后呢再写另外一个函数检测,低于乘就说检测。
13:08
低于检测,检测成绩低于60分的输出不合格的成绩,我跟大家来看一下这段代码是怎么运行用的啊,代码非常简单,基本上就是几句话的事,就是一个for循环,没有什么需要逻辑上的东西,就是直接告诉你们是怎么用的就可以了,来给大家演示一下。同学们跟上老师思路,我呢先把这个注销。我们再写,对,我们再写一个案例,那么这个叫动态分配,我就叫M,好吧,叫M扣。什么呢?DEMO好吧。那现在呢,我们在这里。呃,我们在这里注意听讲哈,这些都不都不难。在哪里呢?我刚才写的是。Mo大头的,这然后呢,我们给大家来写一下这个代码,它是。
14:01
一个怎样的一个含义,画一个内存图大家就明白了。好看一下。那现在呢,这边我们需要引入对引入include。ASTIO。好,这个就OK了。那么这这个函数,它到底是在做一件什么事情呢?我们重点要分析前面的啊,我们要分析前面的这句话。啊,这我们一句说吧,我们一句一句一句给大家解释好不好,认真听讲哈。就讲一次就可以了,因为这些呢,讲一次大家就知道是怎么玩的了。好,然后呢,我在这边给它来一个浅色纯色。OK,没问题吧,同学们,那现在呢,我们在这里插入一个内存。还得用这个来说话,好变成一个黑色的好,现在呢,我们这边有一个站。这是我们的站,明明白哈,这是我们的站,是不是还有一个堆啊哦,因为现在呢,动态分配的数据呢,都是默认放在堆区的,好,这是堆,这是站,这是我们的站,看懂了,这是我们的堆区。
15:09
看懂了哈堆区看懂,那现在的代码呢,开始执行了,首先第一句话。这句话是VO的check in的心,这个其实就是一个声明,就是一个函数声明。这个很很简单,就是函数一个声明,我声明了一下,为什么要声明呢?因为我把这个函数定义写在下边的,如果我这个check函数我是写在面的,连声明都不需要就可以用,就不需要这句话了,我这样用的是因为我把这个函数定义写在下面的,所以说我要先声明才能使用这个check,下面这个地方就不说了,这边有一个P,还有个I,好,也就是说在我们的这一个主站里边,大家可以想象。在我们这个主函数里边这样理解哈,在我们主函数里面呢,有一个有一个P。
16:00
对,有一个P,这个P呢是一个指针,现在不知道指向哪里。对吧,好,然后呢,还有一个ii呢,呃,后面会赋值的I,现在呢,也不知道一个具体的值。先空在这里,好,下面继续执行下一句话,这句话很重要,这个用了个my local啊,就啊,My local my local呢,里面五乘以size of什么意思啊,是不是开辟了。一个20个字节的空间,这个空间呢,会开辟到哪里呢?开辟到这个地方。OK,这个地方会有一个空间,这个空间里面有20个字节。那么大家知道字节肯定也是咱们如果想象的话,也是一个一个的点嘛。对不对,如果我这样画的话,就应该有20个。有20个这样的这这样的小框,一个小框呢,代表一个字节,好吧,就代表一个字节。
17:00
啊,但是我这没有没办法画那么多,大家大家知道就行了,画一下吧。上面已经有1234567,呃七那不够画了,我只有只有换行了啊。啊,这样子整排整排来吧。整拍对吧,1234567整拍了一个。这又是一排是连续的啊,是连续的,这边排布完了过就往下面这一排来,我们再来一个七个的。往这边挪一下,那现在20个应该多了一个它那么呃,这个时候是不是我们第一个字节,就第一个字节其实是有地址的,这个能理解吗。诶,这个你你这个20个字节,第一个字节是有个地址吗?每个字节都有个地址吗?好,那么这个地址呢,我给大家写到这来。0X。好听,咱们这儿这儿写。好0X,比如说0X1122,好,我把这个地址呢,放在哪里呢?放在这。
18:05
把这个放这啊。相当于说这个这个地址。我这儿整一个小的。小的一个曲线。这样大家呢,看起来就不会乱了,相当说我们第一个字节地址是0X112,当然后面这个加一个字节就加嘛,就这个就应该是0X1123,这个是0X1124,这个是0X1125哈,我就不去写那么多了,没时间啊,下面这个做完了功能这个P看清楚了啊,这个P呢,你看它直接把它就转成了本身这边各位各位同学,本身这个返回的类型是一个VO。贸易的心。但是这个贸易的心呢,我要我我是希望能够用这个,呃,这个P来接收,所以说我在这强转了一下。强壮了,像高版本的其实可以不写这句话,C99这个版本其实可以不写,它会自动的实现VO的新转付给这个int新的这个过程,但是呢,这个要C99的版本,我们这个VSVSVS2012啊,没有没有到这个2C99这个版本,所以说我们这边是需要强转一下的,否则呢,它会报错。
19:22
好,那这样子的话就意味着什么呢?就意味着我们这个地方有个地址是0X1122,明白了吧。也就是说我们在这个地方呢,有一个地址指向,也指向它了,指向我们动态开辟的这个空间的第一个址字节,当然它本身也有地址啊,这你上它是一个指针,肯定它自己还有一个地址,比如说0X也1177吧。这样子画一个。好,这样看懂了吗?好,然后呢,这个就写完了for循环,大家看这个for循环呢。其实他获循环五次,是不是在这个地方就给它赋值啊。
20:00
是不是就给它赋值啊,但是这个赋值大家有有注意观察,因为你这个地方虽然是20个字节,虽然20个字节,但是你是以什么呢?以你是以呃四个int来组成的,所以说这四个字节呢,它其实是啊,这20个字节实际上是按五个字节来管理的。也就是说你也就是说我们这个地方,这个P是int类型的一个,Int类型指向四嘛,四个,所以说它是这样一组。就这样一组,这是一个,这下面这个是一组,下面又是一组,这样一组,说到这20个字节,如果我们画的更清楚一点呢,其实可以理解成是什么呢。哦。二二二十个字节对的,20个字节确实是这样子的啊,那20个字节其实和五个int,因为一个字节占四个嘛,对不对,所以说我这地方可以画成这样子,画的更简单一点,就这样画了哈,我我这里就不画这么多了,因为实际上20个字节和20个字节合的是五个int。
21:08
是不是五个int啊。哎,我给你的,那这样子呢,我们就不画这么长了,我就保留前面这几个好吧,这些我就不要了,能理解吧。这些我就不要了,因为这样子我画的太多了呢,反而不好,不好去写这个东西,好,然后我们把这个呢拉长一点。就相当于说我这里有五个int。注意啊,现在一个小黑点,一个小黑点就就升级了,就代表了代表了一个int了,就不再是呃一一个字节了,好。把这个我们挪一下。外面挪一下啊。现在一个框框代表的是四个字节及一个int,明白了吗?好,然后呢,大家看P1 P加一,P加一这个呃嗯,P加ii等于零的时候,就是P本身,那么这个时候你在输,往里面输的时候,这边本身就是要求一个地址嘛,所以说SKY f100分到D,这个P就是地址,那相当于说给它复值,给我们的第一个音头复值,那这个地方你输什么就什么,比如说我们输的是个十,那么这边存的就是个十,明白好,这个循环完了过后呢,这个I加加就是P加一,P加一就在这个地方来了,比如说我们输了个20。
22:30
好在P加三,P加二,P加二就是这个值就30。好,假设我们给的是30,这边给一个40。好,这边给一个事实,这边呢,我们再给一个50就可以了,待会这样子就完成了,紧接着我们再调用check p check p,注意这个地方呢,它会产生一个新的站,注意听啊,这个刚开始还是有点绕的,这玩意。啊,我把这个往这个往上面来一点。好,这个呢,我把它弄到顶层上面去哈。
23:02
就是大家看。就是呃,当我们调用这个的时候呢,这个check,呃函数。这个函数呢,它是把我们把这个P,把这个P传给了check。里面的这个P,也就是说在这个函数函数里面呢,它也有一个指针呢,这个指针它是怎么存的呢?它其实是把这个地址,就是把把我们这个main函数的一个P的这个地址。对,附到了我们这个check函数这个地方来,当然了他自己也有一个地址,这是肯定的,比如说0X1199。好,也就是说,呃,也就是说我们这个check check这个函数呢,它这个P也指向了我们这个堆的这个,呃嗯,开辟的这个空间的第一个啊,第一个嗯嗯,地址就说白了就是指向第一个元素啊,因为它是找到这个开辟空间的首地址嘛,就是我们第一个元素,那么这样子的话,代码就进到这面来了,代码进到这里面呢,诶我就现在进行一个统计,怎么统计呢?我也便利了五次,实际上就是要去看这个这个这个这个这个是不是有没有及格的。
24:23
好,怎么变例子呢?如果这个PI看PI。同学们看这里。如果这个PI。哎,同学们看,PI小于60。我们就输出对,因为你你现在这个时候取的时候呢,实际上是。这是一个指针,我们PI是实际上是可以直接取到这个值的,大家还记不记得哦,我们前讲过这个东西,所以说PI呢,PIP0就取得了这个值。P1就取得这个值,P2就取得这个值,P3就取得这个值,P4就取得这个值了。
25:04
哦,明白这个道理了,那也就是说我们通过这个P下标,我们就可以来取出相应的这个值,进行进行这个进行这个检查,好,我们来试一下,看看是不是这样的道理哈,来我们玩一把,看代码能不能。跑起来。来同学们,我们运行一下哈,啊对,后面还有一个free t。呃,如果我们这个代码都走完的话呢,后面又执行那个freep,这个freep是干什么呢,它会销毁。销毁什么呢?就是我们这一个堆区,注意听销毁堆区。堆曲。堆曲这个P指向的这个空间,也就是说这个P它指向的是这个空间有20个,那么他就会把这里面这20个空间的地址干什么呢?给你销毁了。
26:00
就没有了,相当于诶也也也可以理解成是我们这个整个整个数据就被回收了啊,当然你这回收了过后,不管是check函数还是我们的主函数都不能再使用这个数据了,理解了吧,就我用完了过后呢,我就不要了。诶,这就是free,好,现在我们来玩一把吧,看看代码是不是能够像我们想象的那样跑起来,好吧,来运行一下。来走一个运行,我们看前面有没有没有注销的,跑起来哈,来运行一个。OK。好不及格的成绩呢,我检查一个输出一个。好,现在按我们刚才的那个来来走,输一个十,我们看是怎么输,输进去的是一个一个输的还是什么啊,一个一个输。说第一个十。20。30。四十五十,那看啊,我这一回车,那么这五个数,这五个数就会填在我们这个堆区里面的,呃,开辟的数据空间里面去一回车。
27:10
啊,不及格的有这么写全部检测出来了,那么这次呢,我们输,呃,输几个及格的人数,看看代码到底是不是正确的好吧,输一个十,输一个60,输一个90,输一个30,输一个嗯100。那看我现在呢,哎,这个不行,这样不能这样输出啊,因为我们这个是一回车一回车的啊一个。啊,输一个60,输一个90,输一个30,输一个100,好同学们看,现在我及格的是60 90和100回车。好,不及格的呢,只有十和30正确的。正确的好,这就是老师给大家讲的,这个的使用也并不是很难,对不对,我把这个稍微的注释一下。啊,其实都不用注释了,因为我们在讲的时候,其实就已经讲过了,对不对,这边就讲过了,好这边有个销毁P,把这个拿过来。
28:07
这个是销毁P纸箱的空间。好,这边呢,这边说一下,就是这边会开在堆区,在堆区。对区开辟一个五乘以四的一个数据空间空间并将这个地址,将这个地址将地址。啊,这个他原先这个地址啊,My my local这个地址的类型是void。啊,贸易的星把这个地址呢,转成了,哎,转成了一个什么呢,转成了这一个星啊T星。付给谁呢?哎,付给了。付给。呃,付给谁了呢?付给这个P。好,就是这么一个意思哈,这么一个意思,好了同学们,那关于这个我们叫做动态内存分配的一个内容,就说到这儿,大家理解一下。
我来说两句