网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。
一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!
4.realloc函数(重点)
realloc——重新分配动态内存空间大小(可大可小)
简简单单
总结:
参数:
ptr: 是要调整的内存地址
size: 调整之后新
大小
返回值为调整之后的内存起始位置。
1.如果返回一个新的地址,这个函数调整原内存空间大小的基础上,还会将原来内存中的数据拷贝到新 的空间
。
2.realloc调整失败,将返回一个空指针,并且参数ptr所指向的内存块不会被释放
(它仍然有效,其内容不变)。
3.如果新内存大小更大,则新分配的部分的值是不确定的
4.如果ptr是一个空指针,realloc等价于malloc,分配一个大小为字节的新块,并返回一个指向其开头的指针
realloc在调整内存空间的是存在两种情况:
- 情况1:原有空间之后有足够大的空间,要扩展内存就直接原有内存之后直接追加空间,原来空间的数据不发生变化,返回的地址与原地址(ptr)相同。
- 情况2:原有空间之后没有足够多的空间时,扩展的方法是:在堆空间上另找一个合适大小的连续空间来使用。这样函数返回的是一个新的内存地址,且将
原来内存中的数据拷贝到新的空间,且原来的旧空间被释放
,但新分配的那一部分还是未初始化的,值不确定
使用realloc重新分配空间时一定要先拿一个临时指针来接收返回值,不能直接用原来空间的指针来接收,若realloc 分配空间失败会返回一个NULL直接用原来空间的指针来接收会导致原来的空间直接找不到啦。
如果ptr是一个空指针,realloc等价于malloc,分配一个大小为字节的新块,并返回一个指向其开头的指针
三.动态内存分配迷之操作
1.对NULL指针的解引用操作
所以实验malloc函数最好要检查是否开辟内存成功
2.对动态开辟空间的越界访问
3.对非动态开辟内存使用free释放
free函数的说明
如果参数 ptr 指向的空间不是动态开辟的,那free函数的行为是未定义的。
4.使用free释放一块动态开辟内存的一部分
5.对同一块动态内存多次释放
当然我觉得没有人紧挨着释放两次,但是就怕你释放之后不记得又释放一次,最后的解决办法就是释放之后将指针置为NULL指针,free(NULL),不会有任何问题。
这样即防止了p变成野指针,也可以防止对同一块动态内存重复释放
6.动态开辟内存忘记释放(内存泄漏)
图中可以看到程序运行后,疯狂吃电脑内存。
四.经典笔试题
- 代码1
思考以下代码有什么问题**
改正:
思路一:传地址
思路二:返回给str接收到的动态分配的地址
两种方式记得后面要释放动态内存
- 代码2:
- 代码3
五.揉性数组
结构体中的最后一个元素
允许是未知大小的数组,这就叫做柔性数组成员
。
柔性数组的特点:
1.结构中的柔性数组成员前面必须
至少一个其他成员
。
2.sizeof 返回的这种结构大小不包括柔性数组的内存。
3.包含柔性数组成员的结构用malloc ()函数进行内存的动态分配
,并且分配的内存应该大于结构的大小
,以适应柔性数组的预期大小。
sizeof 返回的这种结构大小不包括柔性数组的内存
柔性数组开辟空间
使用
//柔性数组
int main()
{
printf("%d\n",sizeof(struct S));
struct S \* tmp;
int i=0;
struct S \*p=(struct S\*)malloc(sizeof(struct S)+5\*sizeof(int));
if (p==NULL)
{
return 0;
}
for(i=0;i<5;i++)
{
p->arr[i]=i;
}
tmp=(struct S\*)realloc(p,sizeof(struct S)+10\*sizeof(int));
if (tmp !=NULL)
{
p=tmp;
tmp=NULL;
}
for(i=5;i<10;i++)
{
p->arr[i]=i;
}
for(i=0;i<10;i++)
{
printf("%d ",p->arr[i]);
}
free(p);
p=NULL;
return 0;
}
其实柔性数组完全可以用一个结构体指针去指向一块动态内存开辟的空间代替,也是后面我们实现顺序表的方法
int main()
{
int i=0;
int \*tmp;
struct S s;
struct S \*ps=&s;
ps->arr=(int \*)malloc(5\*sizeof(int));
if (ps->arr ==NULL)
{
return 0;
}
for(i=0;i<5;i++)
{
ps->arr[i]=i;
}
tmp=(int \*)realloc(ps->arr,10\*sizeof(int));
if (tmp !=NULL)
{
ps->arr=tmp;
tmp=NULL;
}
for(i=5;i<10;i++)
{
ps->arr[i]=i;
}
for(i=0;i<10;i++)
{
printf("%d ",ps->arr[i]);
}
free(ps->arr);
ps->arr=NULL;
return 0;
}
其实动态顺序表你把上面这个搞懂了那还不是简简单单。
六.动态顺序表
如果你认真看完了本文章
《结构体详解》
《指针从入门到熟练掌握》
又看完了上面两篇,动态顺序表还搞不定,直接来打我不解释
顺序表是用一段物理地址连续
的存储单元依次存储数据元素的线性结构,一般情况下采用数组存储。在数组上完成数据的增删查改。
SquList.h
#ifndef \_\_SEQLIST\_H
#define \_\_SEQLIST\_H
#include <stdio.h>
#include<stdlib.h>
typedef int SQDataType;
typedef struct SeqList
{
SQDataType \*arr;//动态顺序表
int size;//当前表中元素个数
int capacity;//当前表容量
}SeqList;
//初始化顺序表
void SeqListInit(SeqList \*ps);
//打印顺序表
void SeqListPrint(SeqList \*ps);
//清除顺序表
void SeqListClear(SeqList \*ps);
//尾插
void SeqListPushBack(SeqList \*ps,SQDataType x);
//头插
void SeqListPushFront(SeqList \*ps,SQDataType x);
//尾删
void SeqListPopBack(SeqList \*ps);
//头删
void SeqListPopFront(SeqList \*ps);
//任意位置插入一个元素
void SeqListInsert(SeqList \*ps,int pos,SQDataType x);
//任意位置删除一个元素
void SeqListErase(SeqList \*ps,int pos);
#endif /\* \_\_SEQLIST\_H \*/
SquList.c
#include "SeqList.h"
//初始化顺序表
void SeqListInit(SeqList \*ps)
{
ps->arr=(SQDataType \*)malloc(10\*sizeof(SQDataType));
ps->capacity=10;//初始化表容量为10元素
ps->size=0;//当前表中有多少个元素
}
//打印顺序表
void SeqListPrint(SeqList \*ps)
{
int i=0;
for(i=0;i<ps->size;i++)
{
printf("%d ",ps->arr[i]);
}
}
//清除顺序表
void SeqListClear(SeqList \*ps)
{
ps->size=0;
}
//检测容量
static void CheckCapacity(SeqList \*ps)
{
if (ps->size == ps->capacity)
{
SQDataType \*tmp;
//容量不够利用realloc函数,扩容至原来的两倍
tmp=(SQDataType \*)realloc(ps->arr,2\*ps->capacity\*sizeof(SQDataType));
if (tmp != NULL)
{
ps->arr=tmp;
}
ps->capacity=2\*ps->capacity; //容量为原来的两倍
}
}
//尾插
void SeqListPushBack(SeqList \*ps,SQDataType x)
{
//添加元素前都要检测容量
CheckCapacity(ps);
ps->arr[ps->size]=x;
ps->size++;
}
//头插
void SeqListPushFront(SeqList \*ps,SQDataType x)
{
int i=0;
CheckCapacity(ps);
for(i=ps->size-1;i>=0;i--)
{
ps->arr[i+1]=ps->arr[i];
}
ps->arr[0]=x;
ps->size++;
![img](https://img-blog.csdnimg.cn/img_convert/2d5448dde198af0ca2fef9151cf39faf.png)
![img](https://img-blog.csdnimg.cn/img_convert/e410d9d54d8205f4219920fbcc170b95.png)
![img](https://img-blog.csdnimg.cn/img_convert/ec8264b589632c42b34626cd8f118171.png)
**既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,涵盖了95%以上Go语言开发知识点,真正体系化!**
**由于文件比较多,这里只是将部分目录截图出来,全套包含大厂面经、学习笔记、源码讲义、实战项目、大纲路线、讲解视频,并且后续会持续更新**
**[如果你需要这些资料,可以戳这里获取](https://bbs.csdn.net/topics/618658159)**
[i+1]=ps->arr[i];
}
ps->arr[0]=x;
ps->size++;
[外链图片转存中...(img-7VexX0EN-1715562836105)]
[外链图片转存中...(img-Cn9MLgMP-1715562836106)]
[外链图片转存中...(img-MtY9krp5-1715562836106)]
**既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,涵盖了95%以上Go语言开发知识点,真正体系化!**
**由于文件比较多,这里只是将部分目录截图出来,全套包含大厂面经、学习笔记、源码讲义、实战项目、大纲路线、讲解视频,并且后续会持续更新**
**[如果你需要这些资料,可以戳这里获取](https://bbs.csdn.net/topics/618658159)**