1.指针
2.结构体
3.动态内存开辟
这3个决定你学数据结构的水平,这3个你学不好的话到时候用c++来实现数据结构体会很teng的
目前我们知道的向内存申请空间的方法
1.一种是申请变量的空间
2.一种是申请数组的空间
我们这个申请有什么不好的呢,就在于你申请了这个空间之后,这个空间是无法改变的,
空间一旦开辟好久不能调整他的大小了
知道为什么会有动态内存管理存在吗
原因
1.空间开辟的大小是固定的
2.数组在申明的时候必须指定数组大小的长度,它所需要的内存在编译时分配,不仅仅是上面的情况,有时候我们在程序编译的时候,如果空间不够了,然后我们的数组的大小是指定的,那这样就需要我们的动态开辟了
动态内存管理核心的东西就是下面这4个函数
1.malloc
2.calloc
3.realloc
4.free
malloc
开辟内存块的函数
开辟一块size字节的空间,返回这个空间的起始位置
要怎么用
当我们要开辟多少个字节时,就往malloc里面写多少个字节,比如我们要开辟40个·字节的空间,就直接写40,开辟好了空间之后,就要返回这个空间的起始地址,哎呀但是为什么返回的地址是无符号的呢,因为你开辟这个空间,你可能想好了这个空间以后是用来干什么的,但是这个函数是不知道你想干什么的,所以他返回的无符号,并且如果你想用来开辟char和int都可以
但是我们要想一想,我们申请内存空间的时候会不会申请失败
会失败,你想想你用户入如果要4个字节,但你只开辟了3个字节,那么不就失败了吗,失败了就会返回一个空指针
现在我们要开辟40个字节的空间来存放整形
那么我们就用一个整形指针来接收,并且把他开辟的空间,强制转换为整形
这样以后我们给p加1的话就会跳过一个整形
在我们C语言中
INT MAX里面有整形最大开辟的空间
21亿多的空间
这时候就开辟失败了
我们数组和变量是在栈区开辟的空间
我们的动态内存的函数开辟空间是在堆区的
栈区里面的空间如果你申请的不够大是不能再申请的
堆区的不够大就可以再申请一点点
有学习过的小伙伴肯定知道,我们用了malloc申请,那么不就要释放吗,不然的话是不是内存泄漏了
这里我是没有写free,但是这也代表了不了我没有回收,当我的程序退出的时候,我们的系统会自动帮我们回收内存空间
内存泄漏:你向我申请空间,我把空间分给你,你用完了你不再用但你没有还,你不用不还,我就拿不到那本书,那么就内存泄漏
比如你我借你一本书,你看完了但你就是拿着不还,这本书我看不上,你不还,那么就是内存泄漏了
变长数组
变长数组这个名字不好,因为他不能把这个数组的长度变长,他的作用就是当你在指定他大小的时候可以用便来你
vs编译器是是支持的,c99编译器上才支持
这里我们还是写一下释放,free,但是我们还给系统的时候这个指针存的还是我们的地址,所以我们要手动给他个空指针,如果我们不给他个空指针,那么P如果有招一日拿起来,去访问我们这个空间,那么这个p就是野指针了,所以为了防止野指针,我们给他个空指针,让他永远都不要找到我们
这样的写法是不行,因为你p都被你赋值成空指针了,你连p的空间你都找不到,你还释放个毛啊
先释放再清除
如果你非要写bug无节制的开辟空间,那么电脑就会一直给你分配空间,然后最后结果就会死机
但是现在的电脑太智能,是不会给你无节制的开辟空间的,以前的电脑就会,直接卡到死机了
malloc和free注意事项
malloc
这个函数向内存申请一块连续可用的空间,并返回指向这块空间的指针
如果开辟成功,则返回开辟的内存空间的指针
如果开辟失败,则返回空指针,因此malloc的值一定要做检查
返回的类型是void *所以malloc不知道要开辟的空间的类型,具体在使用的时候由使用者来决定
如果size的参数是0 malloc行为的标准是未定义的,取决于编译器
free
是用与释放动态开辟的内存空间的
如果ptr指向的空间不是动态内存开辟的,那free的行为是未定义的
就是有人这样写,真是无语了
free必须释放动态内存开辟的空间,像我们这个p是栈区上的空间,不能想当然的去释放别人的空间
如果free释放的是空指针,那么就什么都不干
接下来我们来calloc
num是多少个元素
size就是每个元素的大小
假设我们要开辟40个字节
我们就可以换着说开辟10个元素,大小为4字节的空间
注意我们的calloc在开辟好空间之后,会把开辟的空间初始化为0,而我们之前学的malloc就不行
里面都是随机值
相当于这样
动态动态,现在必须要动动了
realloc
有时候我们申请的空间太小了,不够用,有时候我们申请的空间太大了,浪费了,为了合理的使用的内存,所以我们出现了这个函数
ptr就是我们要修改的空间的起始地址
size就是你希望把他调整为多大的空间
比如这样
realloc的工作原理
第一中情况,假设我们在内存中开辟了40个字节,然后我们的内存空间其他地方有些内存已经被用掉了
后面有别人要用的空间,而我们追加的字节数40可能会把别人的空间给覆盖了
第二种情况,就是比别人的情况好一点,后面没有别人用的空间,我们就可以直接追加
第一种情况怎么办,他是不会把别人的空间覆盖掉的,所以他自己会开辟一块新的空间
他会开辟一个新的空间放80个字节,然后把旧的数据拷贝过来,拷过来之后,再返回一个新的地址回去,旧的空间不用你手动释放,realloc会自动帮你释放的
千万不要用p去接收,因为如果你扩容失败了,那么就会变成野指针,因为你原先旧的地址已经是被释放了,
要拿一个新的指针去接收,如果这个指针没接收到野指针那么就可以被接收,说明扩容成功了
发现我们扩容后的地址还是一样的说明是第一种情况
注意如果你频繁的使用realloc会导致内存的碎片化,这里一块内存,哪里一块内存,内存利用率下降,然后还会导致效率下降,当你开辟新的空间,旧的就要释放影响效率
每次我们使用realloc申请空间,都是向堆区申请的,我们的堆区是调佣操作系统的接口去申请空间的,每一次申请空间都要打断我们操作系统的执行然后去申请空间的,所以你频繁去申请,效率就会低,碎片多
这里写了个空指针就跟malloc是一样的了
见的动态内存错误
1.动态内存的野指针
前面如果p开辟没成功,收到了个空指针,如果你没判断是否是空指针,那么就会变成野指针
2,动态开辟内存的越界访问
3.对非动态内存的开辟内存使用free函数
是指向栈区的空间,是不能释放的
4.使用free释放一块动态内存开辟内存的一部分
‘
’
找到最后一块地址,释放但是不行,释放必须全部释放
5.对同一块空间多次释放
不能多写,如果你想要多写的话,第二次就给p赋值一个空指针
这样才可以多写,当我们free里面放空指针时相当于什么都没干
6.动态内存开辟忘记释放
当这个值等于5,这个内存就释放不了,这个程序就找不到这个内存了
p是一个指针,str传过去,p这个指针开辟一块放NULL,接下里malloc开辟100个字节的空间的起始地址赋值给p,
然后p返回主函数的时候销毁了,这个临时变量销毁了,然后开辟100个字节的起始地址就找不到了,存在了内存泄漏,str就是空指针了,拷贝函数会报错,打印都打印不了
修改‘’
临时创建的变量,返回地址,当他的变量销毁,他就是个野指针
指针不初始化就会造成随机访问,如果把一个值放到这个指针变量里面就是野指针