【C语言】动态内存管理基础知识——动态通讯录,如何实现通讯录容量的动态化,C语言动态内存管理实现通讯录容量动态化详解

马肤

温馨提示:这篇文章已超过462天没有更新,请注意相关的内容是否还可用!

摘要:,,本文介绍了C语言中的动态内存管理基础知识,并以实现动态通讯录为例,详细阐述了如何实现通讯录容量的动态化。通过动态内存分配,可以在程序运行时根据需要动态地创建和删除数据,从而避免预先分配固定大小的内存所带来的限制。在实现动态通讯录时,通过malloc和free函数进行内存的分配和释放,根据通讯录中联系人的数量动态调整内存大小,提高了程序的灵活性和可扩展性。

引言

动态内存管理的函数有:malloc,calloc,ralloc,free,本文讲解动态内存函数和使用,如何进行动态内存管理,实现通讯录联系人容量的动态化,对常见动态内存错误进行总结。

        

 【C语言】动态内存管理基础知识——动态通讯录,如何实现通讯录容量的动态化,C语言动态内存管理实现通讯录容量动态化详解 第1张               ✨ 猪巴戒:个人主页✨

               所属专栏:《C语言进阶》

        🎈跟着猪巴戒,一起学习C语言🎈

目录

引言

为什么存在动态内存分配

malloc

动态内存空间位置

 内存泄漏问题

free

calloc   

realloc 

realloc如何开辟动态内存空间

动态版本的通讯录

常见动态内存错误

1.对NULL空指针进行解引用

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

3.对非动态开辟的内存使用free释放

4.使用free释放动态开辟内存的一部分

5.对同一块动态内存多次释放

6.动态开辟内存忘记释放(内存释放)

动态通讯录的实现



为什么存在动态内存分配

int main()
{
    int arr[20] = {0};//整形数组开辟20个元素,就是80个字节。
    return 0;
}

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

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

如果我们开辟的空间不够,那么进行修改会比较麻烦。如果开辟的空间较大,那么所占据的空间又会很大。那么有没有一种方法可以用多少空间就开辟多大的内存呢?

这个时候就有了动态内存开辟。


malloc

动态内存函数的头文件 

void* malloc (size_t size)

【C语言】动态内存管理基础知识——动态通讯录,如何实现通讯录容量的动态化,C语言动态内存管理实现通讯录容量动态化详解 第2张

申请内存块,

size

就是我们要申请的字节大小。当然,我们的内存是有限的,不是想要申请多少内存就可以申请多少内存。

返回值

  • 如果开辟成功,则返回一个指向开辟好空间的指针。
  • 如果开辟失败,则返回一个NULL指针,因此malloc的返回值一定要做检查。
  • 返回值的类型是void*,所以malloc函数并不知道开辟空间的类型,具体在使用的时候使用者自己来决定。

    开创的空间放回地址是void*类型,使用的时候记得要强制类型转换。

    所以我们在使用malloc函数时,要检测返回值p是否是空指针。

    #include 
    #include 
    #include 
    #include
    int main()
    {
    	int arr[10] = { 0 };
    	//动态内存开辟
    	int* p = (int*)malloc(40);
    	if (p == NULL)      //检测返回的p是不是空指针。
    	{
    		printf("%s\n", strerror(errno));
    		return 1;
    	}
    	//使用动态内存
    	int i = 0;
    	for (i = 0; i  
    

    【C语言】动态内存管理基础知识——动态通讯录,如何实现通讯录容量的动态化,C语言动态内存管理实现通讯录容量动态化详解 第3张

             

    动态内存空间位置

     动态内存函数是在堆区开辟内存空间的。我们一般的局部变量、形式参数都是存放在栈区。

    【C语言】动态内存管理基础知识——动态通讯录,如何实现通讯录容量的动态化,C语言动态内存管理实现通讯录容量动态化详解 第4张

             

     内存泄漏问题

    什么是内存泄漏?

    我们创建的局部变量,数据会在函数结束时释放。

    动态内存空间存放临时使用的数据,这些数据不必等到函数结束时释放,而是需要时随时开辟,不需要时,随时释放。动态开辟的内存使用完是要进行释放的,如果不对内存进行释放,那么开辟的动态内存就会被之前的数据占据,这部分的内存就无法使用,相当于丢失了内存。因此我们把这类问题叫做内存泄漏。

    下面的代码如果吧进行释放,那么就会一直占据内存空间。

    【C语言】动态内存管理基础知识——动态通讯录,如何实现通讯录容量的动态化,C语言动态内存管理实现通讯录容量动态化详解 第5张

     通过释放动态内存,内存可以被重新调用

    【C语言】动态内存管理基础知识——动态通讯录,如何实现通讯录容量的动态化,C语言动态内存管理实现通讯录容量动态化详解 第6张

    最后我们加了一步,p = NULL,p的内存空间已经被释放了还给操作系统了,但是p还是原来的地址,我们通过p就会使用到一个已经释放的内存,这就会导致野指针问题。为了避免这种情况,我们将p的地址去掉就可以了。

    【C语言】动态内存管理基础知识——动态通讯录,如何实现通讯录容量的动态化,C语言动态内存管理实现通讯录容量动态化详解 第7张

            

    free

    void free (void* ptr)

    free函数是专门用来释放动态开辟的内存。

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

      free函数只能释放动态内存开辟的空间,如果释放其他空间,就会报错。

      #include 
      int main()
      {
      	int a = 0;
      	int* p = &a;
      	free(p);
      	p = NULL;
      	return 0;
      }

      【C语言】动态内存管理基础知识——动态通讯录,如何实现通讯录容量的动态化,C语言动态内存管理实现通讯录容量动态化详解 第8张

              

      calloc   

      void* calloc (size_t num, size_t size)

      【C语言】动态内存管理基础知识——动态通讯录,如何实现通讯录容量的动态化,C语言动态内存管理实现通讯录容量动态化详解 第9张

      num 

      开辟空间元素的个数

      size

      空间中每个元素的大小。

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

        下面的例子就是解释:

        #include 
        #include 
        #include 
        #include 
        int main()
        {
        	int* p = (int*)calloc(10, sizeof(int));
        	if (p == NULL)
        	{
        		printf("%s\n", strerror(errno));
        	}
        	//打印
        	int i = 0;
        	for (i = 0; i  
        

        【C语言】动态内存管理基础知识——动态通讯录,如何实现通讯录容量的动态化,C语言动态内存管理实现通讯录容量动态化详解 第10张

                

        realloc 

        void* realloc (void* ptr, size_t size)

        【C语言】动态内存管理基础知识——动态通讯录,如何实现通讯录容量的动态化,C语言动态内存管理实现通讯录容量动态化详解 第11张

        ptr

        要调整的空间的起始位置。

        size

        • realloc函数的出现让动态内存管理更加灵活。
        • 有时我们发现过去申请的空间太小了,有时候我们又会觉得申请的空间豁达了,那为了合理的内存,我们一定对内存的大小做灵活的调整。那realloc函数可以做到对动态开辟内存大小的调整。
          #include 
          #include 
          #include 
          #include 
          int main()
          {
          	int* p = (int*)malloc(40);
          	if (p == NULL)
          	{
          		printf("%s\n", strerror(errno));
          	}
          	//使用
          	int i = 0;
          	for (i = 0; i data, 0, sizeof(pc->data));
          }

          动态通讯录要实现开辟3个联系人的空间,并将它们进行初始化。 既要开辟空间,又要进行初始化,我们想到calloc函数。

          malloc是单纯地开辟空间,realloc是既开辟空间,并进行初始化。malloc和realloc地区别就在于是否对开辟的空间初始化。

          开辟3个联系人空间,calloc进行开辟,将地址传给pc->data.将记录联系人的容量传给capacity.

          int InitContact(Contact* pc)
          {
          	assert(pc);
          	pc->count = 0;
          	pc->data =(int*) calloc(3, sizeof(PeoInfo));
          	if (pc == NULL)
          	{
          		printf("InitContact::%s\n", strerror(errno));
          		return 1;
          	}
          	pc->capacity = 3;
          	return 0;
          }

           2.增加联系人

          参数pc,是创立的struct Contact结构体变量的地址,这里是传址调用,作用就是改变原来的数据。

          动态内存管理,通过pc->data[count]可以进行数据的输入。最重要的是实现通讯录容量的动态化。

          count表示已经使用的联系人数量,capacity表示联系人的总容量。

          当count == capacity时,就要动态开辟内存,对容量进行增容。

          CheckCapacity为自定义增容函数,我们要实现增容的功能。

          realloc重新开辟内存块,实现内存的动态化。realloc返回的起始地址不能直接传给data,因为动态内存的开辟有可能失败,失败传回空指针。

          capacity增加2,最后提示增容成功。

          void CheckCapacity(Contact* pc)
          {
          	if (pc->count == pc->capacity);
          	{
          		PeoInfo* ptr = (PeoInfo*)realloc(pc->data, (pc->capacity + 2) * sizeof(PeoInfo));
          		if (ptr == NULL)
          		{
          			printf("AddContact::%s\n", strerror(errno));
          			return 1;
          		}
          		else
          		{
          			pc->data = ptr;
          			pc->capacity += 2;
          			printf("增容成功\n");
          		}
          	}
          }
          void AddContact(Contact* pc)
          {
          	assert(pc);
          	//增容
          	CheckCapacity(pc);
          	printf("请输入名字:》");
          	scanf("%s", pc->data[pc->count].name);
          	printf("请输入年龄:》");
          	scanf("%d", &(pc->data[pc->count].age));
          	printf("请输入性别:>");
          	scanf("%s", pc->data[pc->count].sex);
          	printf("请输入电话:>");
          	scanf("%s", pc->data[pc->count].tele);
          	printf("请输入地址:>");
          	scanf("%s", pc->data[pc->count].addr);
          	pc->count++;
          	printf("增加成功\n");
          }

          将整体的代码呈现在文章末尾。

                  

          常见动态内存错误

                  

          1.对NULL空指针进行解引用

          如果开辟的空间过大,malloc有可能开辟失败,开辟失败就会返回空指针。如果直接对p进行解引用,就会产生问题。

          #include 
          int main()
          {
          	int* p = (int*)malloc(40);
          	*p = 20;
          	return 0;
          }

          正确解决方法: 

          在开辟动态内存后对p进行检验,是否为空指针。

          #include 
          #include 
          #include 
          #include 
          int main()
          {
          	int* p = (int*)malloc(40);
          	if (p == NULL)
          	{
                  printf("%s\n", strerror(errno));
          		return 1;
          	}
          	*p = 20;
          	free(p);
          	p = NULL;
          	return 0;
          }

                  

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

          我们只开辟了10个字节的空间,但是访问,从0到10,10算进去的话就是11个元素,这里访问越界了,就会出问题。

          #include 
          #include 
          #include 
          #include 
          int main()
          {
          	int* p = (int*)malloc(40);
          	if (p == NULL)
          	{
          		printf("%s\n", strerror(errno));
          		return 1;
          	}
          	//使用
          	int i = 0;
          	for (i = 0; i count = 0;
          	pc->data =(int*) calloc(DEFAULT_SZ, sizeof(PeoInfo));
          	if (pc == NULL)
          	{
          		printf("InitContact::%s\n", strerror(errno));
          		return 1;
          	}
          	pc->capacity = DEFAULT_SZ;
          	return 0;
          }
          void DestroyContact(Contact* pc)
          {
          	assert(pc);
          	free(pc->data);
          	pc->data = NULL;
          }
          void CheckCapacity(Contact* pc)
          {
          	if (pc->count == pc->capacity);
          	{
          		PeoInfo* ptr = (PeoInfo*)realloc(pc->data, (pc->capacity + INC_SZ) * sizeof(PeoInfo));
          		if (ptr == NULL)
          		{
          			printf("AddContact::%s\n", strerror(errno));
          			return 1;
          		}
          		else
          		{
          			pc->data = ptr;
          			pc->capacity += INC_SZ;
          			printf("增容成功\n");
          		}
          	}
          }
          void AddContact(Contact* pc)
          {
          	assert(pc);
          	//增容
          	CheckCapacity(pc);
          	printf("请输入名字:》");
          	scanf("%s", pc->data[pc->count].name);
          	printf("请输入年龄:》");
          	scanf("%d", &(pc->data[pc->count].age));
          	printf("请输入性别:>");
          	scanf("%s", pc->data[pc->count].sex);
          	printf("请输入电话:>");
          	scanf("%s", pc->data[pc->count].tele);
          	printf("请输入地址:>");
          	scanf("%s", pc->data[pc->count].addr);
          	pc->count++;
          	printf("增加成功\n");
          }
          void ShowContact(const Contact* pc)
          {
          	assert(pc);
          	int i = 0;
          	//一个汉字是两个字符
          	printf("%-20s\t%-5s\t%-5s\t%-12s\t%-30s\n", "名字", "年龄", "性别", "电话", "地址");
          	for (i = 0; i count; i++)
          	{
          		printf("%-20s\t%-5d\t%-5s\t%-12s\t%-30s\n", pc->data[i].name,
          			pc->data[i].age,
          			pc->data[i].sex,
          			pc->data[i].tele,
          			pc->data[i].addr);
          	}
          }
          static int FindByName(Contact* pc, char name[])
          {
          	assert(pc);
          	int i = 0;
          	for (i = 0; i count; i++)
          	{
          		if (0 == strcmp(pc->data[i].name, name))
          		{
          			return i;
          		}
          	}
          	return -1;
          }
          void DelContact(Contact* pc)
          {
          	char name[MAX_NAME] = { 0 };
          	assert(pc);
          	int i = 0;
          	if (pc->count == 0)
          	{
          		printf("通讯录为空,没有信息可以删除\n");
          		return;
          	}
          	printf("请输入要删除人的名字:>");
          	scanf("%s", name);
          	//删除
          	//1.查找
          	int pos = FindByName(pc, name);
          	if (pos == -1)
          	{
          		printf("要删除的人不存在\n");
          		return;
          	}
          	//2.删除
          	for (i = pos; i count; i++)
          	{
          		pc->data[i] = pc->data[i + 1];
          	}
          	pc->count--;
          }
          void SeachContact(Contact* pc)
          {
          	assert(pc);
          	char name[MAX_NAME] = { 0 };
          	printf("请输入需要查找的联系人的名字:>");
          	scanf("%s", name);
          	//1.查找
          	int pos = FindByName(pc, name);
          	if (pos == -1)
          	{
          		printf("要查找的人不存在\n");
          		return;
          	}
          	//2.打印
          	printf("%-20s\t%-5s\t%-5s\t%-12s\t%-30s\n", "名字", "年龄", "性别", "电话", "地址");
          	printf("%-20s\t%-5d\t%-5s\t%-12s\t%-30s\n", pc->data[pos].name,
          		pc->data[pos].age,
          		pc->data[pos].sex,
          		pc->data[pos].tele,
          		pc->data[pos].addr);
          }
          void ModifyContact(Contact* pc)
          {
          	assert(pc);
          	char name[MAX_NAME] = { 0 };
          	printf("请输入需要查找的联系人的名字:>");
          	scanf("%s", name);
          	//1.查找
          	int pos = FindByName(pc, name);
          	if (pos == -1)
          	{
          		printf("要查找的人不存在\n");
          		return;
          	}
          	printf("要修改人的信息已经找到,接下来进行修改\n");
          	//2.修改
          	printf("请输入名字:》");
          	scanf("%s", pc->data[pos].name);
          	printf("请输入年龄:》");
          	scanf("%d", &(pc->data[pos].age));
          	printf("请输入性别:>");
          	scanf("%s", pc->data[pos].sex);
          	printf("请输入电话:>");
          	scanf("%s", pc->data[pos].tele);
          	printf("请输入地址:>");
          	scanf("%s", pc->data[pos].addr);
          	printf("修改成功\n");
          }
          int cmp_peo_by_name(const void* e1, const void* e2)
          {
          	return strcmp(((PeoInfo*)e1)->name, ((PeoInfo*)e2)->name);
          }
          //按照名字来排序
          void SortContact(Contact* pc)
          {
          	assert(pc);
          	qsort(pc->data, pc->count, sizeof(PeoInfo), cmp_peo_by_name);
          	printf("排序成功\n");
          }

          test.c

          通讯录主脉络:

          #define _CRT_SECURE_NO_WARNINGS
          #include "contact.h"
          void menu()
          {
          	printf("**********************************************\n");
          	printf("********   1.add            2.del     ********\n");
          	printf("********   3.search         4.modify  ********\n");
          	printf("********   5.show           6.sort    ********\n");
          	printf("********   0.exit                     ********\n");
          	printf("**********************************************\n");
          }
          int main()
          {
          	int input = 0;
          	Contact con;
          	//初始化通讯录:模块化初始化
          	InitContact(&con);//只能传地址,进行修改
          	do
          	{
          		menu();
          		printf("请选择:》");
          		scanf("%d", &input);
          		switch (input)
          		{
          		case 1:
          			AddContact(&con);
          			break;
          		case 2:
          			DelContact(&con);
          			break;
          		case 3:
          			SeachContact(&con);
          			break;
          		case 4:
          			ModifyContact(&con);
          			break;
          		case 5:
          			ShowContact(&con);
          			break;
          		case 6:
          			SortContact(&con);
          			break;
          		case 0:
          			DestroyContact(&con);
          			printf("退出通讯录\n");
          			break;
          		default:
          			printf("选择错误\n");
          		}
          	} while (input);
          	return 0;
          }
          

0
收藏0
文章版权声明:除非注明,否则均为VPS857原创文章,转载或复制请以超链接形式并注明出处。

相关阅读

  • 【研发日记】Matlab/Simulink自动生成代码(二)——五种选择结构实现方法,Matlab/Simulink自动生成代码的五种选择结构实现方法(二),Matlab/Simulink自动生成代码的五种选择结构实现方法详解(二)
  • 超级好用的C++实用库之跨平台实用方法,跨平台实用方法的C++实用库超好用指南,C++跨平台实用库使用指南,超好用实用方法集合,C++跨平台实用库超好用指南,方法与技巧集合
  • 【动态规划】斐波那契数列模型(C++),斐波那契数列模型(C++实现与动态规划解析),斐波那契数列模型解析与C++实现(动态规划)
  • 【C++】,string类底层的模拟实现,C++中string类的模拟底层实现探究
  • uniapp 小程序实现微信授权登录(前端和后端),Uniapp小程序实现微信授权登录全流程(前端后端全攻略),Uniapp小程序微信授权登录全流程攻略,前端后端全指南
  • Vue脚手架的安装(保姆级教程),Vue脚手架保姆级安装教程,Vue脚手架保姆级安装指南,Vue脚手架保姆级安装指南,从零开始教你如何安装Vue脚手架
  • 如何在树莓派 Raspberry Pi中本地部署一个web站点并实现无公网IP远程访问,树莓派上本地部署Web站点及无公网IP远程访问指南,树莓派部署Web站点及无公网IP远程访问指南,本地部署与远程访问实践,树莓派部署Web站点及无公网IP远程访问实践指南,树莓派部署Web站点及无公网IP远程访问实践指南,本地部署与远程访问详解,树莓派部署Web站点及无公网IP远程访问实践详解,本地部署与远程访问指南,树莓派部署Web站点及无公网IP远程访问实践详解,本地部署与远程访问指南。
  • vue2技术栈实现AI问答机器人功能(流式与非流式两种接口方法),Vue2技术栈实现AI问答机器人功能,流式与非流式接口方法探究,Vue2技术栈实现AI问答机器人功能,流式与非流式接口方法详解
  • 发表评论

    快捷回复:表情:
    评论列表 (暂无评论,0人围观)

    还没有评论,来说两句吧...

    目录[+]

    取消
    微信二维码
    微信二维码
    支付宝二维码