动态内存管理

一.为什么要有动态内存分配

我们已经掌握的内存开辟方式有:

int a = 10;
char arr[10] = { 0 };

但是上述的开辟空间的方式有两个特点:

  1. 空间开辟的大小是固定的
  2. 数组在申请的时候,必须指定数组的长度,数组的空间一旦确定了大小是不能调整的。

但是对于空间的需求,不仅仅是上述的情况。有时候我们需要的空间大小在程序运行的时候才能知道,那数组的编译方式就不能满足了。

C语言引入了动态内存开辟,这样我们自己可以根据情况自行申请和释放空间,就比较灵活。

动态内存是计算机中一种特殊的内存,用于存储在程序运行时创建和使用的数据。动态内存通过使用堆来分配和管理内存。与静态内存相比,动态内存的大小和生命周期不是在编译时确定的,而是在运行时根据程序的需求进行动态分配和释放。动态内存的使用可以提供更大的灵活性和效率,但也需要程序员负责手动管理内存的分配和释放,以避免内存泄漏和访问错误。

 二.mallocfree

1.malloc函数

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

void* malloc(size_t size);

这个函数所需的头文件是<stdlib.h> 

 malloc的m指的是内存(memory),alloc指的是allocate分配的意思。

这个函数的功能就是分配内存块。它的参数是size_t size.

malloc会分配size个字节的内存块,并且返回这个块的起始地址,由于不知道这个块所指向的数据的类型是什么,所以返回值的类型是 void *.

注:这个新分配的内存块的内容是不会被初始化的,也就是这个空间中的值是不确定的,也就是随机值。

	//分配20个字节的空间,来存放5个整形
    int* p = (int*)malloc(20);
	if (p == NULL)
	{
		perror("malloc");
		return 1;
	}
	//使用空间
	int i = 0;
	for (i = 0; i < 5; i++)
	{
		*(p + i) = i + 1;
	}

 malloc函数的返回值的类型是void*,这样的类型是不能被解引用使用的,所以我们需要根据情况来对返回值进行类型转化,这里我需要它存放整形的变量,我就把它转化为int*类型的指针,并且用一个int*类型的变量p来接收。

malloc函数在向内存申请一块空间时,如果开辟成功,就会返回这个空间的指针,但是当我们开辟失败的时候,这个函数就会返回NULL指针,所以我们在使用malloc函数时,一定要对返回值进行检查。

一般来说我们在使用malloc函数的时候,我们是对我们所需要的空间的大小来进行申请的,这个大小不能是负数,一般情况下,也不能是0,如果是0,就没有必要申请了。如果我们非要传递0作为这个函数的参数,这个函数的返回值就是标准未定义的了(有可能是NULL,也可能不是),它会取决特定库来执行,不同的编译可能有区别。所以这样的情况下,这个空间是不能解引用的。

一般来说,我们申请空间是不会失败的,但是也有可能失败。

比如:

	int* p;
	while(1) {
		p = (int*)malloc(INT_MAX);
	if (p == NULL)
	{
		perror("malloc");
		return 1;
	}

 如果我们一直申请空间,导致最后空间不足,就会申请失败。

最后就会打印空间不足。 

2.free函数 

 C语言提供了另一个函数free,专门用来做动态内存的释放与回收的,函数原型如下:

void free(void* ptr);

这个函数的声明也在<stdlib.h>中 

我们使用malloc函数申请了一块空间后,即使我们不再使用,如果我们不释放它,那么这块空间是不会被再次malloc的,而free函数就是将这些之前被malloc函数申请的空间释放掉,使其可以被再次malloc使用,不然如果我们一直malloc,空间就会不足了。

所以我们在上面写的代码均是有问题的,我们再使用过后,应该将这段空间释放掉。

	int* p = (int*)malloc(4);
	if (p == NULL){
		perror("malloc");
		return 0;
	}
	*p = 4;
	printf("%d ",*p);
	free(p);
	p = NULL;
	return 0;

在我们释放掉p所指向的空间后,p的值是不改变的,但是这块空间我们已经归还给操作系统了,我们无法使用,但是p还指向这个空间,这就是野指针,为了避免这样的情况我们就需要人为将p置为NULL;

如果ptr指针指向的不是动态内存开辟的空间,那么free函数造成的结果是未定义的。

观察这个代码是否有问题?

	int* p = (int*)malloc(10 * sizeof(int));
	if (p == NULL){
		perror("malloc");
		return 0;
	}
	for (int i = 0; i < 10; i++)
	{
		*p = i;
		printf("%d ",*p);
		p++;
	}
	free(p);
	p = NULL;
	return 0;

 答案是有。

在我们使用free函数的时候有一个很重要的点,就是free函数的参数必须是这块空间的起始地址,而上面的代码的p已经不是我们申请的空间的起始地址了。所以我们对这个动态开辟的内存空间释放失败了。

这一点非常重要,free的参数所指向的必须是动态内存开辟的空间的起始地址。

如果我们提供的参数是NULL,那么free函数什么都不会做。

三.calloc和ralloc

1.calloc函数

C语言还提供了一个函数叫calloc,calloc函数也用于动态内存分配。原型如下:

void* calloc (size_t num, size_t size);

 这个函数的声明也是在<stdio.h>中

这个函数的功能和malloc函数很相似,作用是为num个大小为size字节的元素开辟一块空间,并且将这个空间的值初始化为0.

也就是说,第一个参数是元素个数,第二个参数是元素大小。开辟的总空间是num * size 个字节。

实际上calloc函数与malloc函数的主要区别就是对内存进行了初始化。

	//使用malloc的情况
	//int*p = (int*)malloc(5 * sizeof(int));
	//if (p == NULL)
	//{
	//	perror("malloc");
	//	return 1;
	//}
	
	//使用calloc函数
	int* p = (int*)calloc(5, sizeof(int));
	if (p == NULL)
	{
		perror("malloc");
		return 1;
	}
	int i = 0;
	for (i = 0; i < 5; i++)
	{
		printf("%d ", *(p + i));
	}
	free(p);
	p = NULL;
	return 0;

如果size的值是0,那么这个函数的返回值是未定义的,(可能是NULL,也可能不是)。这样的返回值的指针是不能被解引用的。

2.realloc函数 

顾名思义,realloc函数的作用就是重新分配内存空间。

realloc函数的出现让动态内存管理变大更加灵活。

有时候我们会发现我们之前申请的空间大小太小了,或者太大了造成空间浪费,这时候我们就可以使用realloc函数对这个内存大小做出一定的调整。

函数原型:

void* realloc (void* ptr, size_t size);

 ptr指向的是动态内存开辟的空间的起始地址,size指的是我们对其进行调整后所需要的大小。

返回值是调整后的内存的起始地址。

在我们进行重新分配的过程中,是有两种情况的,如果这个空间后面的空间还没有被使用,那么就接着在后面开辟。

如果后面的空间已经被分配了,这时候不能接着后面开辟了,这时候这个函数就会寻找一个新的空间来满足这个大小,并且将原来内存中的数据移动到新的空间中。

由于存在上面的这两种情况,我们在使用这个函数时需要更加注意了。

	int* ptr = (int*)malloc(100);
	if (ptr == NULL)
	{
		perror("malloc");
	}

	//拓展内存
	//代码1 - 直接将realloc的返回值放到ptr中
	ptr = (int*)realloc(ptr, 1000);//这样可以吗?
	//如果分配失败了怎么办?返回值是NULL.
	//我们不仅没有分配到新的空间,而且还会丢失掉原来空间的地址,无法进行释放了。

	//代码2 - 先将realloc函数的返回值放在p中,不为NULL,在放ptr中
	int* p = NULL;
	p = realloc(ptr, 1000);
	if (p != NULL)
	{
		ptr = p;
	}
	//业务处理
	free(ptr);
	return 0;

在我们进行重新分配时,这个size可能比原来大也可能比原来小。

  1. 情况1:新的size小于原先的size.
    如果这个内存空间被移动到了新的位置,那么原来内存块中的值也会跟着移动,但是由于size变小了,所以只会保留新的size这么大的内存空间的值。
  2. 情况2:新的size大于原先的size
    如果这个内存空间被移动到了新的位置,那么原来内存空间的值也会跟着移动,但是size变大了,足以保存原先的值。而新分配的空间的值就是不确定的,随机值。

如果realloc函数的第一个参数是NULL,那么这个函数就和malloc函数一模一样,分配size个字节的空间并返回起始地址。

这个函数分配的空间位置变化了,原先的空间就会被自动free掉了,不需要我们人为free。

 如果分配空间时失败了,那么这个函数会返回一个空指针,并且被参数ptr所指向的空间不会被释放掉,这个空间仍然有效,值也不会变,可以被访问。

对于上面三个函数来说,他们所分配的空间均要使用free函数来进行释放,如果没有释放,程序结束的时候操作系统也会回收,尽量要做到这三个函数和free函数成对出现。

  • 19
    点赞
  • 17
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值