动态内存管理(C语言)

目录

一、为什么存在动态内存管理?

二、C语言中动态内存管理方式

1、函数介绍(malloc、calloc、realloc、free)

1)先来看malloc函数

2)calloc函数

3)realloc函数

2、动态内存管理中的错误操作


一、为什么存在动态内存管理?

	int var = 10;
	int arr[10];

上面变量及数组的空间开辟为固定的大小:分别是 4 字节和 40 字节。

1、如果我们在为像栈或是链表这样的数据存储结构开辟空间时,就有可能会在空间不足的情况下为其开辟空间,那么就会涉及到动态内存的开辟。这也就是动态内存管理存在的原因之一。

2、换言之,有的时候我们并不能一次性的开辟好需要的空间大小,如果一次性开辟空间太大会造成空间的浪费,开辟的太小又会不太够用;这时候我们就希望可以有一种动态的方法:当需要使用内存时,需要多少? 立马为其开辟多少,不需要我就不开辟空间,这样既可以满足需求还可以尽可能少的避免造成空间的浪费。

二、C语言中动态内存管理方式

1、函数介绍(malloc、calloc、realloc、free)

前三个函数的使用往往会与free函数搭配起来使用,前者是在内存上开辟所需空间提供使用,后者是在对空间使用结束后释放、归还系统的。

1)先来看malloc函数

void* malloc (size_t size);

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

#include
#include
int main()
{

	int arr[10] = { 0 };
	int* ptr = NULL;
	ptr = (int*)malloc(10 * sizeof(int));
	if (NULL == ptr)   //判断ptr指针是否为空
	{
		printf("malloc fail!\n");
		exit(-1);
	}
	int i = 0;
	for (i = 0; i<10; i++)
	{
		*(ptr + i) = i;
	}

	for (i = 0; i<10; i++)
	{
		printf("%d ", *(ptr + i));
	}
	printf("\n");
	free(ptr);   //释放ptr所指向的动态内存
	ptr = NULL;
	return 0;
}

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

void free (void* ptr);

free函数用来释放动态开辟的内存:(在上面的代码也有所体现)
        如果参数 ptr 指向的空间不是动态开辟的,那free函数的行为是未定义的。
        如果参数 ptr 是NULL指针,则函数什么事都不做。

2)calloc函数

void* calloc (size_t num, size_t size);

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

#include
#include
int main()
{

	int* ptr = NULL;
	ptr = (int*)calloc(10 , sizeof(int));
	if (NULL == ptr)   //判断ptr指针是否为空
	{
		printf("malloc fail!\n");
		exit(-1);
	}
	int i = 0;
	for (i = 0; i<10; i++)
	{
		printf("%d ", *(ptr + i));  // 全为0
	}
	printf("\n");
	free(ptr);   //释放ptr所指向的动态内存
	ptr = NULL;
	return 0;
}

输出结果: 0 0 0 0 0 0 0 0 0 0

3)realloc函数

realloc函数的出现让动态内存管理更加灵活。
有时会我们发现过去申请的空间太小了,有时候我们又会觉得申请的空间过大了,那为了合理的时
候内存,我们一定会对内存的大小做灵活的调整。那 realloc 函数就可以做到对动态开辟内存大小的调整。
函数原型如下:

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

ptr 是要调整的内存地址,size 调整之后新大小,返回值为调整之后的内存起始位置。
这个函数调整原内存空间大小的基础上,还会将原来内存中的数据移动到的空间。
realloc在调整内存空间的是存在两种情况:
情况1:原有空间之后有足够大的空间
此时扩展内存会在原有空间的后面直接增加,原内存存储数据不变。

#include
#include
int main()
{
	int* ptr = NULL;
	ptr = (int*)malloc(10 * sizeof(int));
	if (NULL == ptr)   //判断ptr指针是否为空
	{
		printf("malloc fail!\n");
		exit(-1);
	}
	int i = 0;
	for (i = 0; i<10; i++)
	{
		*(ptr + i) = i;  
	}
	int* ptr1 = NULL;
	ptr1 = (int*)realloc(ptr, 80);
	if (NULL == ptr1)   //判断ptr指针是否为空
	{
		printf("realloc fail!\n");
		exit(-1);
	}
	for (i = 0; i<10; i++)
	{
		*(ptr1 + i + 10) = i+10;  
	}
	for (i = 0; i<20; i++)
	{
		printf("%d ", *(ptr1 + i));  
	}
	printf("\n");
	free(ptr1);  //释放ptr所指向的动态内存
	ptr1 = NULL;
	return 0;
}

上述代码realloc在原来malloc开辟空间的基础上扩展空间,原空间之后有足够大的空间。所以指针ptr和ptr1的地址是相同的。如下图所示:

动态内存管理(C语言)_第1张图片

情况2:原有空间之后没有足够大的空间                                                                                            此时会在堆空间寻找一处足够大小的连续空间,并返回此处空间的地址。

动态内存管理(C语言)_第2张图片

当realloc在原来空间的大小上新开辟的空间太大时,即原有空间之后没有足够大的空间 ;此时会在堆空间寻找一处足够大小的连续空间,并返回此处空间的地址。同时也会释放原空间。(将上面代码段中realloc开辟空间的大小调的大点可以验证)

2、动态内存管理中的错误操作

在动态内存管理中所开辟的空间使用结束后需要对其进行释放操作,目的是为了将空间还给内存供系统调用。但需要注意以下错误操作:

1)对NULL进行解引用。

2)越界访问开辟的空间。

int main()
{
	int* ptr = NULL;
	ptr = (int*)malloc(10 * sizeof(int));
	int i = 0;
	for (i = 0; i < 10; i++)
	{
		*(ptr + i) = i;
	}
	printf("%d\n", *(ptr + 5));  // 正确访问
	printf("%d\n", *(ptr + 15));  // 越界访问
	return 0;
}

3)对非动态开辟的空间进行free操作。

4)只释放部分动态开辟空间。

int main()
{
	int* ptr = NULL;
	ptr = (int*)malloc(10 * sizeof(int));
	int i = 0;
	for (i = 0; i < 10; i++)
	{
		*(ptr + i) = i;
	}
	ptr++;
	free(ptr);
	ptr = NULL;
	return 0;
}

5)对同一动态开辟的内存空间多次释放。

6)忘记释放动态开辟的内存空间。(内存泄漏)

结论:动态开辟的内存空间使用结束后一定要释放,并且要正确释放。

★★★★★ 感谢阅读!!!

你可能感兴趣的:(C语言,c语言)