【C语言】小王带您轻松实现动态内存管理(简单易懂)
创始人
2024-05-12 08:50:58
0

在上文通讯录制作中,动态通讯录的使用中就用到了动态内存管理,如果有同学想看一看是如何运用的内存管理函数的,请参考这篇文章,接下来我们一起学习动态内存管理的相关知识。【C语言】使用C语言实现静态、动态的通讯录(简单易懂)_小王学代码的博客-CSDN博客

目录

前言

一、动态内存函数有那些?

1.1 malloc和free

1.2 calloc

1.3 realloc

1.3.1 realloc调整内存空间的时候有两种情况:

二、常见动态内存错误(案例分析)

2.1 对于NULL指针的解引用操作

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

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

2.4 使用free释放了动态开辟内存的一部分

2.5 对同一块动态内存进行多次释放

2.6 动态开辟空间忘记释放(内存泄漏)

三、练习题

3.1 第一个

3.2 第二个

3.3 第三个

3.4 第四个

总结


前言

我们已经掌握的内存开辟的方法有两种

int a = 10;      //在栈空间上开辟4个字节的空间

int a[10] = {0};  //在栈空间上开辟40个字节的连续空间

这些开辟方式都有两个共同的特点:

1.空间开辟大小是固定的

2.数组在申明的时候,必须指定数组的长度,它需要的内存在编译的时候分配

我们为什么要实现动态管理内存呢,这又什么作用呢?

我们对于空间的需求不仅仅只是上面两种,有时候我们到底需要多少空间,需要运行之后才能知道,这个时候就需要动态开辟内存空间,即动态内存函数就诞生了!

一、动态内存函数有那些?

1.malloc和free

2.calloc

3.realloc

1.1 malloc和free

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

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

1.如果开辟成功,则返回一个指向开辟好空间的指针。

2.如果开辟失败,则返回一个NULL指针,因此malloc的返回值一定要做检查。 3.返回值的类型是 void* ,所以malloc函数并不知道开辟空间的类型,具体在使用的时候使用者自己来决定。 4.如果参数 size 为0,malloc的行为是标准是未定义的,取决于编译器。

void*的返回类型,使用的时候根据情况强制类型转换

C语言还提供free函数,专门是用于做动态内存的释放和回收的,函数原型如下:

free函数是用来释放动态开辟的内存:

1.如果参数 ptr 指向的空间不是动态开辟的,那free函数的行为是未定义的。(会报错)

2.如果参数 ptr 是NULL指针,则函数什么事都不做。

图文演示:

头文件要加上 malloc.h 

 代码演示:

int main()
{int num = 0;scanf("%d", &num);//int arr[num] = { 0 };   num 在 [] 中//VS 不支持这样,但是可以使用动态内存函数,实现动态数组int* ptr = (int*)malloc(sizeof(int) * num);if (NULL == ptr) {//进行判断是否创建成功perror("malloc::ptr");	}else {for (int i = 0; i < 10; i++) {*(ptr + i) = i;}for (int i = 0; i < 10; i++) {printf("%d ", *(ptr + i));}free(ptr);  //使用free函数释放动态申请的ptrptr = NULL;  //将ptr  free之后,置为NULL,防止野指针非法访问}return 0;
}

而且malloc函数创建的空间不会进行初始化,里面存放的是随机值,如图

1.2 calloc

calloc函数也是C语言提供的,用来动态内存分配,原型如下:

calloc函数介绍:

1.函数的功能是为 num 个大小为 size 的元素开辟一块空间,并且把空间的每个字节初始化为0。 2.与函数 malloc 的区别只在于 calloc 会在返回地址之前把申请的空间的每个字节初始化为全0。

实操图文分析:

 代码演示:

int main()
{int num = 0;int* ptr = (int*)calloc(10, sizeof(int));//使用calloc函数for (int i = 0; i < 10; i++) {*(ptr + i) = i;}for (int i = 0; i < 10; i++) {printf("%d ", *(ptr + i));}free(ptr);//free 动态申请的ptrptr = NULL;//置为NULL,防止野指针越界访问return 0;}

对于calloc动态申请的空间是否每一个字节都变为0呢?我们来看下图

这也是calloc和malloc函数的最大的区别,是否自动初始化,前者有,后者无

1.3 realloc

realloc也是C语言提供的动态内存申请函数,使得动态内存管理更加灵活。本质是可以对已经动态申请过的空间进行增容,是更加灵活的。

有时会我们发现过去申请的空间太小了,有时候我们又会觉得申请的空间过大了,那为了合理的时 候内存,我们一定会对内存的大小做灵活的调整。那 realloc 函数就可以做到对动态开辟内存大小 的调整。

 函数原型如下,并对两个形参ptr和size进行分析:

如上图:

1.ptr可以为NULL,相当于malloc一个新的空间,ptr是要调整的内存地址

2.size同样可以为0,则返回值取决于特定的库实现:它可能是空指针,也可能是不应取消引用的其他位置。size是调整之后的大小

3.返回值为调整之后的内存起始位置。

4.这个函数调整原内存空间大小的基础上,还会将原来的数据移动到新空间。

1.3.1 realloc调整内存空间的时候有两种情况:

第一种情况:当原有空间之后的内存空间足够的时候

第二种情况:当原有空间之后的内存空间不够时

如图所示:

 因为这两种情况是随机发生的,不能控制必须使用哪一种,所以我们就要小心一个事情,不要用原来动态开辟的变量ptr来直接接收realloc,应该创建临时变量接收,先判空,之后再赋值给ptr

代码图示:

 可以自行测试:

int main()
{int* p = (int*)malloc(sizeof(int)*10);if (p == NULL) {perror("malloc::p");}else {printf("%p\n", p);}int* ptr = (int*)realloc(p, sizeof(int) * 20);//创建临时变量
//如果使用 int* p = (int*)realloc(p,....这样的话如果创建失败,返回NULL,
//这样的话p的内容就没有了,所以创建临时变量ptr,然后下面判空之后可以交换if (NULL == ptr) {perror("realloc::ptr");}else {p = ptr;ptr = NULL;printf("%p\n", p);}free(p);p = NULL;return 0;
}

二、常见动态内存错误(案例分析)

2.1 对于NULL指针的解引用操作

意思就是要学会使用动态内存函数的时候吗,要进行判空,不然谁知道有没有问题NULL

int main()
{int* p = (int*)malloc(sizeof(int) * 10);*p = 10;//这个时候谁知道p是不是NULL,如果是NULL,那么这就是非法访问,是错误free(p);return 0;
}

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

就是说,开辟多少空间就是多少空间,不能越过这个字节数的界限访问空间外的地址

int main()
{int* p = (int*)malloc(sizeof(int) * 10);if (NULL == p) {perror("malloc::p");}else {for (int i = 0; i < 100; i++) {*(p + i) = i + 1;//当i等于10的时候就开始越界访问}for (int i = 0; i < 11; i++) {printf("%d ", *(p + i));}free(p);p = NULL;}return 0;
}

和数组一样,不要越界,不需要多想什么额外的东西

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

free可以放置NULL进去,不会报错,但是不能放非动态开辟的内存,会报错

图示分析free函数:

 代码演示:

int main()
{int* p = (int*)malloc(sizeof(int) * 10);int a = 10;free(&a);//非动态内存开辟的,会报错
//free(NULL);  //没有什么反应,程序正常return 0;
}

2.4 使用free释放了动态开辟内存的一部分

就是说如果动态开辟内存之后的p指针的位置发生改变的话再去释放free(p)只是释放一部分

代码演示:

//举例
int main()
{int* p = (int*)malloc(sizeof(int) * 10);p++;free(p);//这个的时候p向右移动一个整型字节空间,再进行释放,那么先前那个空间就没被释放return 0;
}

2.5 对同一块动态内存进行多次释放

多次释放会报错的

图示:

2.6 动态开辟空间忘记释放(内存泄漏)

所以我们要养成当一个动态空间不用的时候就free他,放置内存泄露

代码演示:

int main()
{//test();while (1) {malloc(1);//一直申请就是不释放}
}

三、练习题

3.1 第一个

void GetMemory(char *p)
{p = (char *)malloc(100);
}
void Test(void)
{char *str = NULL;GetMemory(str);
//改为传递地址就可以或者就是用str接收
//str= GetMemory(str);//实际上用临时变量接收更好strcpy(str, "hello world");printf(str);
//用完释放
//free(str);
//str=NULL;
}

1.传值操作,就算p申请了空间也不会使得str发生改变,所以str依旧是NULL,不能有strcpy

2.内存泄漏, GetMemory(str);未释放p的空间 

3.2 第二个

char *GetMemory(void)
{
//修改为:
//static char p[] = "hello world";char p[] = "hello world";return p;
}
void Test(void)
{char *str = NULL;str = GetMemory();printf(str);
}

典型的返回栈地址问题,p数组是局部变量 ,确实是返回了p的地址给str,但是GetMemory函数结束之后,数组p的空间就没有,再访问p的地址(printf(str))就会非法访问

3.3 第三个

void GetMemory(char **p, int num)
{*p = (char *)malloc(num);
}
void Test(void)
{char *str = NULL;GetMemory(&str, 100);strcpy(str, "hello");printf(str);
//修改为:free(str);
//str=NULL;
}

没有释放str动态开辟的空间,没有free(str),str=NULL

3.4 第四个

void Test(void)
{char *str = (char *) malloc(100);strcpy(str, "hello");free(str);
//修改意见:
//str=NULL;if(str != NULL){strcpy(str, "world");printf(str);}
}

在使用str之前就释放了str申请的空间,释放之后str!=NULL,保留原来地址,str这个时候已经是野指针了(因为没有了对相应空间的访问权限),之后确实是输出了world,但是从if语句就已经错误了,置为str=NULL 就可以了

总结

本文主要是对于malloc、calloc、realloc、free函数的介绍和使用细节的说明,还有一些关于动态内存管理的函数,学会了这些,对于以后数据结构的内容会更加得心应手,所以希望大家能多多支持,接下来,下一章,我们跟大家讲解一下,文件管理的内容。学会了就可以更新通讯录啦!!!

相关内容

热门资讯

常用商务英语口语   商务英语是以适应职场生活的语言要求为目的,内容涉及到商务活动的方方面面。下面是小编收集的常用商务...
六年级上册英语第一单元练习题   一、根据要求写单词。  1.dry(反义词)__________________  2.writ...
复活节英文怎么说 复活节英文怎么说?复活节的英语翻译是什么?复活节:Easter;"Easter,anniversar...
2008年北京奥运会主题曲 2008年北京奥运会(第29届夏季奥林匹克运动会),2008年8月8日到2008年8月24日在中华人...
英语道歉信 英语道歉信15篇  在日常生活中,道歉信的使用频率越来越高,通过道歉信,我们可以更好地解释事情发生的...
六年级英语专题训练(连词成句... 六年级英语专题训练(连词成句30题)  1. have,playhouse,many,I,toy,i...
上班迟到情况说明英语   每个人都或多或少的迟到过那么几次,因为各种原因,可能生病,可能因为交通堵车,可能是因为天气冷,有...
小学英语教学论文 小学英语教学论文范文  引导语:英语教育一直都是每个家长所器重的,那么有关小学英语教学论文要怎么写呢...
英语口语学习必看的方法技巧 英语口语学习必看的方法技巧如何才能说流利的英语? 说外语时,我们主要应做到四件事:理解、回答、提问、...
四级英语作文选:Birth ... 四级英语作文范文选:Birth controlSince the Chinese Governmen...
金融专业英语面试自我介绍 金融专业英语面试自我介绍3篇  金融专业的学生面试时,面试官要求用英语做自我介绍该怎么说。下面是小编...
我的李老师走了四年级英语日记... 我的李老师走了四年级英语日记带翻译  我上了五个学期的小学却换了六任老师,李老师是带我们班最长的语文...
小学三年级英语日记带翻译捡玉... 小学三年级英语日记带翻译捡玉米  今天,我和妈妈去外婆家,外婆家有刚剥的`玉米棒上带有玉米籽,好大的...
七年级英语优秀教学设计 七年级英语优秀教学设计  作为一位兢兢业业的人民教师,常常要写一份优秀的教学设计,教学设计是把教学原...
我的英语老师作文 我的英语老师作文(通用21篇)  在日常生活或是工作学习中,大家都有写作文的经历,对作文很是熟悉吧,...
英语老师教学经验总结 英语老师教学经验总结(通用19篇)  总结是指社会团体、企业单位和个人对某一阶段的学习、工作或其完成...
初一英语暑假作业答案 初一英语暑假作业答案  英语练习一(基础训练)第一题1.D2.H3.E4.F5.I6.A7.J8.C...
大学生的英语演讲稿 大学生的英语演讲稿范文(精选10篇)  使用正确的写作思路书写演讲稿会更加事半功倍。在现实社会中,越...
VOA美国之音英语学习网址 VOA美国之音英语学习推荐网址 美国之音网站已经成为语言学习最重要的资源站点,在互联网上还有若干网站...
商务英语期末试卷 Part I Term Translation (20%)Section A: Translate ...