【c语言】动态内存管理

目录

1. 动态内存开辟函数

1.1 malloc函数

1.2 free函数

1.3 calloc函数

1.4 realloc函数

情况1:原有空间之后有足够大的空间

情况2:原有空间之后没有足够大的空间

情况3:调整失败

2. 常见的一些动态内存的错误

3.c/c++中程序内存区域划分

1)栈区(stack):

2)堆区(heap):

3)静态区(数据段):

4)代码段:


一般开辟内存的方式有两个:

int a = 20;//创建变量---在栈空间上开辟4个字节 

char arr[10] = {0};//创建数组---在栈空间上开辟10个字节的连续空间

但是这样开辟空间的大小是固定的,而且数组在申明时必须指定数组的长度,后续也不能调整大小。

事实上,我们对空间的需求有时候是具有灵活性的,远不止上述这两种情况。

c语言引入了动态内存开辟,让程序员可以自己申请和释放空间。

1. 动态内存开辟函数

动态内存开辟相关的函数有这四个:malloc,free,calloc,realloc

他们都声明在stdlib.h头文件中。

我们逐一学习。

1.1 malloc函数

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

56f35757cad54e6995030f0d741356ef.png

如果开辟成功,则返回一个指向开辟好空间的指针。
如果开辟失败,则返回一个 NULL 指针,因此malloc的返回值一定要做检查。


返回值的类型是 void*,所以malloc函数并不知道开辟空间的类型,具体在使用的时候程序员自己来决定。记得根据所需类型强制类型转换


参数类型是size_t,是想要开辟的内存空间大小,单位是字节。如果参数 size为0,malloc的行为是未定义的,取决于编译器。

操作一下:

//假设我们想要开辟的内存空间类型是int,5个整型的大小。
int* ptr = ()malloc(sizeof(int)*5);

1.2 free函数

free函数是专门用来做动态内存的释放和回收的。

967e93a0d82e4a86aa00dd0d5cc64f4c.png

无返回类型。

参数ptr是指针类型,如果ptr指向的空间不是动态开辟的,那free函数的行为是未定义的。

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

操作一下:

int main()
{
	int num = 0;
	scanf("%d", &num);

	//使用数组来申请内存,c语言会报错。
	//int arr[num] = { 0 };

	//使用动态内存的函数malloc
	//动态申请内存
	int* ptr = NULL;
	ptr =(int*) malloc(sizeof(int) * num);
	if (ptr != NULL)//判断ptr是否是空指针
	{
		int i = 0;
		for (i = 0; i < num; i++)
		{
			*(ptr + i) = i;
			printf("%d ", *(ptr + i));
		}
	}
	//释放内存
	free(ptr);
	ptr = NULL;
	return 0;
}

运行结果:

960a7621f9d9457daead90796d0bc24f.png

1.3 calloc函数

calloc函数也用来动态内存分配。

ec73b3df0bde4f33abdb48dc8134d7e8.png

calloc函数的功能是为 num 个大小为 size 的元素开辟一块空间,并且把空间的每个字节初始化为0

返回类型和malloc函数一模一样,取决于程序员自己,记得强制类型转换。

calloc函数与malloc函数的区别只在于 calloc 函数会在返回地址之前把申请的空间的每个字节初始化为0

如果我们对申请内存空间的内容要求初始化,那我们可以很方便地使用calloc函数来实现代码~

操作一下:

代码:

int main()
{
	//申请内存
	int* ptr = (int*)calloc( 5 , sizeof(int));
	if (ptr != NULL)
	{
		for (int i = 0; i < 5; i++)
		{
			printf("%d ", *(ptr + i));//看看calloc函数是否会初始化
		}
	}
	//释放内存
	free(ptr);
	ptr = NULL;
	return 0;
}

结果:

832a1ee06cda45f1aa3fddf4508163b2.png

1.4 realloc函数

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

有时会我们发现过去申请的空间太小了,有时候我们又会觉得申请的空间过大了。那为了合理的使用内存,我们一定会对内存的大小做灵活的调整。

realloc函数就可以做到对动态开辟内存大小的灵活调整。

bbfab63eeb594747a0501b2a625f316d.png

第一个参数ptr是要调整的内存地址。

第二个参数size是调整之后内存的新大小。

返回值是调整之后内存的起始位置。

注意,我们使用realloc函数进行调整内存空间时,会存在三种情况:

情况1:原有空间之后有足够大的空间

e2836c12d45e4d79a48e89d080e6340b.png

此时要扩展内存,就直接在原有内存空间之后追加空间即可,原有空间的数据不发生变化。

情况2:原有空间之后没有足够大的空间

912d9df014154c40aab3968869a92865.png

此时,内存扩展的方法是:在堆空间上另找一个大小合适的连续空间来使用,然后会将原来空间的数据拷贝一份到新的空间,释放旧的空间,最后返回新的内存空间的起始地址。

情况3:调整失败

函数此时返回空指针。

在实际使用realloc函数时,要小心注意一些。

我们操作一下:

int main()
{
	//申请内存
	int* ptr = (int*)malloc(20);
	assert(ptr);

	//扩展内存

	//代码1不推荐
	ptr = (int*)realloc(ptr, 40);


	//更推荐代码2
	int* p = (int*)realloc(ptr, 40);
	if (p != NULL)
	{
		ptr = p;
	}
	else
	{
		return 1;
	}

	//释放内存
	free(ptr);
	ptr = NULL;
	p = NULL;
	return 0;
}

realloc函数可以完成和malloc函数一样的功能:

//realloc函数可以完成和malloc函数一样的功能
int main()
{
	realloc(NULL, sizeof(int)*5);
	//等同于int* ptr=(int*)malloc(sizeof(int) * 5);
	return 0;
}

2. 常见的一些动态内存的错误

罗列一下,程序员使用过程中要小心使用:

1)对NULL指针解引用操作

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

3)对非动态开辟内存使用free函数释放

4)使用free函数释放一块动态开辟内存的一部分

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

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

3.c/c++中程序内存区域划分

简单讲一下c/c++中程序内存区域的划分,程序员在学习中会有更深的理解。

1)栈区(stack):

在执行函数时,函数内局部变量的存储单元都可以在栈上创建,函数执行结束时这些存储单元自动被释放。栈内存分配运算内置于处理器的指令集中,效率很高,但是分配的内存容量有限。 栈区主要存放运行函数而分配的局部变量、函数参数、返回数据、返回地址等。


2)堆区(heap):

一般由程序员分配释放,若程序员不释放,程序结束时可能由0S回收。分配方式类似于链表。


3)静态区(数据段):

(static)存放全局变量、静态数据。程序结束后由系统释放。

4)代码段:

存放函数体(类成员函数和全局函数)的二进制代码。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值