动态内存管理

动态内存管理

  • 一.为什么存在动态内存分配
  • 二.动态内存函数的介绍
    • 1.malloc和free
    • 2.cealloc
    • 3.realloc
  • 三.常见动态内存错误
      • 1.对空指针的解引用操作
      • 2.对动态开辟空间的越界访问
      • 3.对同一块内存空间多次释放
      • 4.动态开辟内存忘记释放
  • 四.经典题目
    • 题目一
    • 题目二
    • 题目三
    • 题目四
  • 五.柔性数组
  • 六.通讯录的动态实现的改变操作

一.为什么存在动态内存分配

1.我们可以定义一个变量去开辟内存空间。
2.定义一个数组去开辟内存空间。
3.但是这些方法是大小固定无法修改的定义好了只有这么多了。

有的时候我们只能在程序执行以后才知道需要多少的空间所有我们就需要动态的开辟我们需要的空间,而且还有一个好处就是节省空间需要多少开辟多少。

二.动态内存函数的介绍

1.malloc和free

函数原型:void * malloc(size_t size)
1.void* 返回类型上面返回类型需要我们自己去进行强制类型转换成我们需要的类型。函数参数是我们需要开辟空间的大小单位是字节;
2.开辟成功的情况下返回的值是开辟空间的首地址。
3.如果开辟失败返回NULL
4.开辟空间有可能会产生开辟空间失败的情况,有一个好习惯就是开辟空间之后进行判断是否为空如果为空说明开辟空间失败了。
5.特殊情况在c语言的标准中没有定义开辟0字节的情况,正常程序没有影响的,同时取决于编译器的。

c语言专门规定了一个函数free专门用来释放回收开辟的空间
函数原型:free(void * ps);
1.如果ps开辟的空间不是动态开辟的,不能用free释放的。
2.如果ps==NULL free(NULL)什么事情都不干。不会产生错误。

malloc和free都在 stdlib.h头文件中

int main()
{
	//静态开辟
	int a = 0;
	int arr[5] = 0;
	//动态开辟
	int* pa =(int *)malloc(sizeof(int));
	int* parr = (int *)malloc(sizeof(int) * 5);

	//动态开辟空间释放
	free(pa);
	free(parr);
	pa = NULL;
	parr = NULL;
	return 0;
}

发现我们的函数malloc函数参数字节不是自己用一个数去规定的而是跟你需要开辟什么样的空间有关,这样的格式sizeof(想要开辟空间类型)* 想要多少这样的空间。
如果free(ps) 但是不去把指针置为NULL的话会怎么样,这个空间已经还给我们的内存空间了但是我们的ps已经指向了一个不属于我们的空间,ps就是一个野指针这种情况是不可以出现的。

2.cealloc

函数原型:void calloc(size_t num,size_t size);
1.calloc 开辟num个大小为size(单位是字节)开辟空间,空间的总大小是num*size(单位是字节)。
2.并且calloc还会把这些空间每个字节都初始化为0.

动态内存管理_第1张图片

动态内存管理_第2张图片

3.realloc

1.realloc的出现使得我们的动态管理更加灵活
2.函数原型:void * realloc(void *ps , size_t size);
3.ps是malloc或者cealloc开辟空间的首地址,size这个空间想要的变成的新的大小单位是字节。
4.返回需要考虑两个情况。

余下空间够:
动态内存管理_第3张图片

余下空间不够:

动态内存管理_第4张图片

例子

#include
#include


int main()
{
	int* parr = (int*)calloc(2,sizeof(int));
	if (parr == NULL)
	{
		printf("calloc fill\n");
		exit(-1);
	}
	int sz = 0;//当前使用空间
	int camp = 2;//当前所有的空间
	while (sz<10)
	{
		if (sz < camp)
		{
			printf("请输入数据:\n");
			//scanf("%d", &parr[sz]);
			scanf("%d", parr+sz);
			sz++;
		}
		else
		{
			//                 原来的         新加入的
			int * pttrr=realloc(parr, sizeof(int) * 2 + sizeof(int) * 2);
			if (pttrr == NULL)
			{
				printf("realloc fill\n");
				exit(-1);
			}
			printf("开辟成功!\n");
			parr=pttrr;
			camp += 2;
		}
	}

	for (int i = 0; i < sz; i++)
	{
		printf("%d ", parr[i]);
	}
	//动态开辟空间释放
	free(parr);
	parr = NULL;
	return 0;
}


int* ptr = (int*)malloc(100);
	if (ptr !=NULL)
	{
		//处理业务
	}
	else
	{
		exit(EXIT_FAILURE);
	}
	//扩展容量
	//代码1
	ptr = (int*)realloc(ptr, 1000);
	//这样可以吗?(如果申请失败会如何?)
	
	//如果申请失败原来malloc的空间会失去
	//代码2
	int* p = NULL;
	p = realloc(ptr, 1000);
	if (p != NULL)
	{
		ptr = p;
	}
	else
	{
		exit(-1);
	}
	//业务处理
	free(ptr);
	ptr = NULL;

三.常见动态内存错误

1.对空指针的解引用操作

int main()
{
	int* p = (int*)malloc(sizeof(int));
	//如果p开辟失败这是一个空指针。
	//对空指针解引用就是错误的所以需要判断空。
	*p = 10;
	free(p);
	p = NULL;
	return 0;
}

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

void test()
{
	int i = 0;
	int* p = (int*)malloc(10 * sizeof(int));
	if (NULL == p)
	{
		exit(EXIT_FAILURE);
	}
	for (i = 0; i <= 10; i++)
	{
		*(p + i) = i;//当i是10的时候越界访问
	}
	free(p);
	p = NULL;
}

int main()
{
	test();
	return 0;
}

3.对同一块内存空间多次释放

动态内存管理_第5张图片

4.动态开辟内存忘记释放

动态内存管理_第6张图片

四.经典题目

题目一

动态内存管理_第7张图片

动态内存管理_第8张图片

动态内存管理_第9张图片

题目二

动态内存管理_第10张图片

动态内存管理_第11张图片

题目三

动态内存管理_第12张图片

动态内存管理_第13张图片

题目四

动态内存管理_第14张图片
动态内存管理_第15张图片

动态内存管理_第16张图片

五.柔性数组

在c99 中结构体中允许最后一个元素是未知大小的数组,这就是柔性数组成员。

动态内存管理_第17张图片

1.结构体的柔性数组前面必须要有一个其他成员。
2.结构体的大小不包括数组的大小。
3.malloc开辟空间的时候需要考虑到这个柔性数组。

typedef struct  add {
	int i;
	char arr[0];//柔性数组成员;
}a;

int main()
{
	a* pa = (a*)malloc(sizeof(a) + sizeof(char) * 4);
	if (pa == NULL)
	{
		printf("malloc fill\n");
		exit(-1);
	}

	scanf("%s", pa->arr);
	printf("%s", pa->arr);

	free(pa);
	pa = NULL;
	return 0;
}
typedef struct st_type
{
	int i;
	int* p_a;
}type_a;
type_a * text(type_a* p)
{
	type_a* p = (type_a*)malloc(sizeof(type_a));
	p->i = 100;
	p->p_a = (int*)malloc(p->i * sizeof(int));
	//业务处理
	for (int i = 0; i < 100; i++)
	{
		p->p_a[i] = i;
	}
	//释放空间
	free(p->p_a);
	p->p_a = NULL;
	free(p);
	p = NULL;
}
int main()
{
	type_a p;
	text(&p);
	return 0;
}

分开开辟的好处:

方便内存释放
如果我们的代码是在一个给别人用的函数中,你在里面做了二次内存分配,并把整个结构体返回给
用户。用户调用free可以释放结构体,但是用户并不知道这个结构体内的成员也需要free,所以你
不能指望用户来发现这个事。所以,如果我们把结构体的内存以及其成员要的内存一次性分配好
了,并返回给用户一个结构体指针,用户做一次free就可以把所有的内存也给释放掉。

连续的内存有益于提高访问速度,也有益于减少内存碎片。

六.通讯录的动态实现的改变操作

1.开辟空间的动态型。
2.空间不够时使用realloc开辟空间。
3.对于info类型结构体的排序传参。

typedef struct infomation {
	char name[NAME];
	int age;
	char tele[TELE];
	char ars[ADDRESS];
}info;


typedef struct address_book {
	info* people;
	int sz;//当前已经有的人的信息
	int caplity;
}list;
//初始化
void Initlist(list* ps)
{
	ps->people = (info*)calloc(2,sizeof(info));
	ps->caplity = 2;
	ps->sz = 0;
}
void Addlist(list* ps)
{
	if (ps->sz == ps->caplity)
	{
		//满了增加空间
		info* ptr =(info*) realloc(ps->people, sizeof(info) * ps->caplity + sizeof(info) * 4);

		if (ptr == NULL)
		{
			printf("realloc fail\n");
			exit(-1);
		}

		ps->people = ptr;
		ps->caplity += 4;
		printf("增容成功\n");
	}

			printf("请输入姓名:");
			scanf("%s", ps->people[ps->sz].name);

			printf("请输入年龄:");
			scanf("%d", &(ps->people[ps->sz].age));


			printf("请输入电话号码:");
			scanf("%s", ps->people[ps->sz].tele);


			printf("请输入地址:");
			scanf("%s", ps->people[ps->sz].ars);

			(ps->sz)++;
	
}
int cmp( void* p1,  void* p2)
{
	return strcmp(( ( (info*)p1)->name), (((info*)p2)->name));
}


//排序
void sqrt(list* ps)
{
	//数组按照年龄排序
	qsort(ps->people, ps->sz, sizeof(info), cmp);
	printf("排序成功!\n");
}

void Destort(list* ps)
{
	free(ps->people);
	ps->people = NULL;
}

#include"address_book.h"

void menu()
{
	printf("******************************\n");
	printf("*****1.add*******2.dle********\n");
	printf("*****3.show******4.find*******\n");
	printf("*****5.dleevery**6.sqrt_name**\n");
	printf("*****0.Exit*******************\n");
}
int main()
{
	list ps;
	Initlist(&ps);

	int inupt = 0;
	do {
		//菜单
		menu();
		printf("请输入你的选择:");
		scanf("%d", &inupt);
		switch (inupt)
		{
			case 1:
				Addlist(&ps);
				break;
			case 2:
				Dellist(&ps);
				break;
			case 3:
				Showlist(&ps);
				break;
			case 4:
				Findlist(&ps);
				break;
			case 5:
				Delevery(&ps);
			case 6:
				sqrt(&ps);
				break;
			case 0:
				break;
			default:
				printf("输入错误!请重新输入!\n");
				break;
		}
	} while (inupt != 0);

	Destort(&ps);
	return 0;
}

你可能感兴趣的:(开发语言,程序人生,学习方法)