动态内存管理

c语言内存分配区域:

在了解动态内存函数之前,我们需要了解c语言是在内存中如何存放变量的。

  1. 栈区(stack):在执行函数时,函数内局部变量的存储单元都可以在栈上创建,函数执行结
    束时这些存储单元自动被释放。栈内存分配运算内置于处理器的指令集中,效率很高,但是
    分配的内存容量有限。 栈区主要存放运行函数而分配的局部变量、函数参数、返回数据、返
    回地址等。
  2. 堆区(heap):一般由程序员分配释放, 若程序员不释放,程序结束时可能由OS回收 。分
    配方式类似于链表。
  3. 数据段(静态区)(static)存放全局变量、静态数据。程序结束后由系统释放。
  4. 代码段:存放函数体(类成员函数和全局函数)的二进制代码。
    在这里插入图片描述

为什么存在动态内存

在我们没掌握动态内存之前,进行内存开辟的方式有:

#include<stdio.h>
int main()
{
	int a=0;
	char b-0;
	int arr[10]={0};
	return 0;
}

int a是在栈区上开辟4个字节的空间;char b在栈区上开辟了1个字节的空间;int arr[10]在栈区上开辟连续的40个字节的空间。

  • 1.开辟空间的方式开辟的空间都是固定的。
  • 2.数组在进行声明时要指定数组的长度。

当我们在程序运行时,当前空间太小,不够用怎么办?当我们确定不了需要开辟的空间怎么办?

struct stu
{
	char name[20];
	int age;
	int grades[10];
}

此时,该学生的姓名过长或者过短,导致空间不足后者空间浪费应该怎么处理呢?
这就需要动态内存开辟空间了。

动态内存函数

malloc:

向内存申请了一块连续可用的空间,并且返回了指向该空间的起始地址的指针。
在这里插入图片描述
从上图中可以看出:

  • 1.malloc接收的参数是size_t(无符号整数,单位是字节)类型的size。
  • 2.malloc返回的类型是void*,所以我们要将malloc强转,还要使用使用指针来接收。
int* ptr=(int*)malloc(10*sizeof(int));

free:

用来释放动态申请的内存。

  • 如果ptr指向的空间不是动态开辟的,那free的行为是不可预知的。
  • 如果ptr是NULL,那么free将什么也不做。
int* ptr=(int*)malloc(10*sizeof(int));
free(ptr);

注意:
在我们使用完动态开辟的空间,一定要记得将动态开辟的空间进行释放,不然可能会导致内存泄漏哦
当我们使用完动态开辟的空间,并且释放掉之后,指向动态开辟的空间的起始得知的指针ptr就是一个野指针,我们要及时的将ptr指向NULL,防止程序出错。

int* ptr=(int*)malloc(10*sizeof(int));
free(ptr);
prt=NULL;

上边说到我们向内存申请了一块空间,会不会出现申请失败呢?

#include<stdio.h>
#include<stdlib.h>
int main()
{
	int* p = (int*)calloc(1000000000, sizeof(int));
	if (p == NULL)
	{
		printf("申请内存失败!\n");
		return;
	}
	printf("申请内存成功!\n");
	free(p);
	return 0;
}

在这里插入图片描述
从上面的例子中我们可以看出,内存申请的空间过大是会申请失败,所以我们需要去判断这次申请的空间是否成功。

#include<stdio.h>
#include<stdlib.h>
int main()
{
	int* p = (int*)malloc(10*sizeof(int));
	if (p == NULL)
	{
		printf("申请内存失败!\n");
		return;
	}
	printf("申请内存成功!\n");
	for (int i = 0; i < 10; i++)
	{
		printf("%d\n",p[i]);
	}
	free(p);
	p = NULL;
	return 0;
}

在这里插入图片描述
从上图中可以看出,我们使用malloc申请的空间并没有进行初始化,而是存放的初始值。
正确的使用方法:

#include<stdio.h>
#include<stdlib.h>
int main()
{
	int* p = (int*)malloc(40);
	if (p == NULL)
	{
		printf("内存开辟失败!\n");
		return;
	}
	for (int i = 0; i < 10; i++)
	{
		*(p + i) = i;
	}
	for (int i = 0; i < 10; i++)
	{
		printf("%d ", *(p+i));
	}
	free(p);
	p = NULL;
	return 0;
}

在这里插入图片描述

注意:

  • 如果malloc开辟空间成功,则返回指向该空间起始地址的指针。
  • 如果malloc开辟空间失败,则返回NULL。
  • 因为返回类型时void*,当我们使用时要注意将malloc返回值强转为我们需要的类型。
  • 使用完动态开辟的空间后要及时的将动态开辟的的空间进行free(释放),防止该空间一直被占用(也许有人会说不free会咋样?当你的程序一直在运行,而你一直将malloc开辟的空间占据,这样下去总有一刻你的内存会满,导致你的程序奔溃)。
  • 为了防止free后指向该malloc开辟的空间的指针变为野指针,我们要将该指针指向NULL。

calloc:

calloc和malloc相似,都是向内存申请了一块连续可用的空间,并且返回指向该空间起始地址的指针。
在这里插入图片描述
从上图可以看到:

  • 1.我们calloc接收的参数有两个:size_t num,size_t size。
  • 1.calloc将向内存申请num个size大小的空间。
    2.calloc返回类型是void*,在使用时要将calloc强转。
int* ptr=calloc(10,sizeof(int));

既然是和malloc一样向内存中申请空间,那么就需要我们来判断该空间是否被成功申请,并且使用完该空间,要及时用free来释放,并且让指向申请空间的起始地址的指针指向NULL。

#include<stdlib.h>
#include<stdio.h>
int main()
{
	int* p = (int*)calloc(10,sizeof(int));
	if (p == NULL)
	{
		printf("内存开辟失败!\n");
		return;
	}

	free(p);
	p = NULL;
	return 0;
}

那么calloc和malloc都是向内存申请空间,那他俩只需要一个就可以了,为什么还要calloc呢?他和malloc有什么区别?

#include<stdlib.h>
#include<stdio.h>
int main()
{
	int* p = (int*)calloc(10,sizeof(int));
	if (p == NULL)
	{
		printf("内存开辟失败!\n");
		return;
	}
	for (int i = 0; i < 10; i++)
	{
		printf("%d\n", *(p + i));
	}
	free(p);
	p = NULL;
	return 0;
}

在这里插入图片描述
从上图中我们可以明显看出,malloc在成功申请到空间后,还将申请到的空间初始化为0。
当我们使用完calloc和malloc申请的空间太小,不够用或者申请的空间太大,我们就需要使用realloc函数来进行调整。

realloc:

当我们使用calloc和malloc申请的空间后,觉得申请的空间太小或者太大,我们可以使用realloc来灵活的调整我们申请的空间的大小。
在这里插入图片描述
从上图中可以看出:

  • 1.realloc接收了两个参数:void* ptr和size_t size。
  • 2.realloc需要指定需要调整空间的起始地址(ptr)和调整后的大小(size)。
  • 3.realloc返回类型是void*,需要将返回的指向调整后的起始空间地址的指针。
#include<stdio.h>
#include<stdlib.h>
int main()
{
	int* ptr = (int*)malloc(10*sizeof(int));
	if (ptr == NULL)
	{
		return 1;
	}
	ptr = realloc(ptr, 15*sizeof(int));
	if (ptr == NULL)
	{
		return 2;
	}
	free(ptr);
	ptr = NULL;
	return 0;

但我们在使用realloc调整空间时他会出现两种调整方法:

  • 1.当原空间后有足够空间,直接在当前空间后继续追加,原来空间的数据不会发生改变。
  • 2.当原空间后没有足够空间,realloc会在堆区上重新找一块合适的,连续的空间,将原空间中的数据拷贝到新空间中,返回指向新空间起始位置的指针。

例如:

#include<stdio.h>
#include<stdlib.h>
int main()
{
	int* ptr = (int*)malloc(40);
	if (ptr == NULL)
	{
		return 1;
	}
	for (int i = 0; i < 10; i++)
	{
		*(ptr + i) = i;
	}
	for (int i = 0; i < 10; i++)
	{
		printf("%d ", *(ptr + i));
	}
	printf("\n");
	int* tmp = realloc(ptr, 1000000);
	if (ptr == NULL)
	{
		return 2;
	}
	ptr = tmp;
	for (int i = 0; i < 10; i++)
	{
		printf("%d ", *(ptr + i));
	}
	free(ptr);
	ptr = NULL;
	return 0;
}

在这里插入图片描述
在这里插入图片描述

我们可以看到ptr地址是0x0132bff0,当我们调整的空间较大时,ptr调整后的空间就变为了0x00f0a040。原空间后的空间不够时,我们会在堆区上重新找一块合适的,连续的空间进行存放。
有人可能会问,当我们的原空间位置不够时,我们realloc会在堆区上重新找一块合适的连续的空间进行开辟,那我们可以用realloc开辟一块没有开辟过的空间呢?

#include<stdio.h>
#include<stdlib.h>
int main()
{
	int* p = (int*)realloc(NULL, 80);
	if (p == NULL)
		return;
	for (int i = 0; i < 10; i++)
	{
		*(p + i) = i;
		printf("%d ", *(p + i));
	}
	free(p);
	p = NULL;
	return 0;
}

在这里插入图片描述
拿realloc开辟一块未被开辟的空间是可以的,不过我们使用realloc开辟一块没有开辟的空间,一定要将空间的起始位置写为NULL。此时,realloc就和malloc的功能相同。

希望以上对您有所帮助!当然,如果文章出现错误,欢迎您在评论区或私信我指出哦~

  • 3
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 5
    评论
评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值