C语言——动态内存管理

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

为什么要进行动态内存管理?

在平常进行变量定义的时候,我们通常是进行如下的定义方式:

int a = 10;//整型变量
int arr[] = {1,2,3,4,5,6,7,8,9,10};//十个元素的整型数组

 但是这样定义出的变量a只能进行存储一个整形变量,以及数组arr也只能存储十个整形,如果想要扩大,那是做不到的,只能重新定义一个,这是相当麻烦的,所以我们便引出了动态内存,可以根据需要来进行扩大,这样不仅节约了空间,也提供了便利。

动态内存函数的介绍

malloc函数:

C语言——动态内存管理_第2张图片 C语言——动态内存管理_第3张图片

#include
//这是包含malloc的头文件
int main() {

	void* p = malloc(10 * sizeof(int));
	//这种当然没什么问题,但是使用者在用的时候肯定是知道自己要开辟什么类型的空间的,
	// 如果用void* P来接收的话,反而不是太方便,所以我们习惯写下面这种写法。

	int* p = (int*)malloc(10 * sizeof(int));

	if (p == NULL) {//这里我们需要判断一下是否申请成功了,
		//如果不进行判断的话,后面会出现解引用NULL指针的问题。
		perror("malloc");
		return 1;
	}
	return 0;
}

那么有人要问了,malloc真的会申请失败么,答案是会的 ️️️

C语言——动态内存管理_第4张图片 看图当我们所申请的值太大时,就会申请失败。 

C语言——动态内存管理_第5张图片

free函数:

C语言——动态内存管理_第6张图片

 当我们动态调用之后,我们有义务对其动态开辟的空间进行释放,也就是说我们向操作系统调了一块空间,我们就必须在不用的时候返还给操作系统,所以free函数是用来将动态开辟的空间释放的。

 C语言——动态内存管理_第7张图片

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

C语言——动态内存管理_第8张图片

我在14行处打一个断点, F5跳到断点处

C语言——动态内存管理_第9张图片

可以看到,地址并没有发生任何变化,但是实际上这块地址已经还给操作系统了,不再属于我们动态开辟的了,所以一定要在释放完之后,进行指针置空,防止后面访问野指针而导致程序错误。 

 calloc函数

C语言——动态内存管理_第10张图片

这是malloc函数的空间开辟,可以看到并没有进行初始化 

C语言——动态内存管理_第11张图片

这是改用calloc函数进行的:

C语言——动态内存管理_第12张图片

#include
#include
int main() {
	int* p1 = (int*)calloc(10 , sizeof(int));
	if (p1== NULL) {
		perror("calloc");
		return 1;
	}
	for (int i = 0; i < 10; i++) {
		printf("%d\n", *(p1+i));
	}
	free(p1);
	p1 = NULL;
	return 0;
}

 realloc函数:

C语言——动态内存管理_第13张图片

C语言——动态内存管理_第14张图片

C语言——动态内存管理_第15张图片 C语言——动态内存管理_第16张图片

 那么正确的方法应该是这样的:C语言——动态内存管理_第17张图片

这样即使realloc申请失败,那也不会影响原本的内存内容。 

当然如果我们在realloc的地址处传一个NULL指针的话,其实它可以等价于malloc

	int* p = (int*)realloc(NULL, 20*sizeof(int));// == malloc

 常见的动态内存错误

对空指针进行解引用操作

int* p = (int*)malloc(40);
//如果不对p进行返回值判断,那么当申请失败时,就会发生解引用操作
*p = 20;

 对动态空间的越界访问

C语言——动态内存管理_第18张图片

当我们的访问超出了申请的内存范围时,就是越界了,这时我们的编译器就会报错。

对非动态开辟内存使用free释放

C语言——动态内存管理_第19张图片

#include
#include
int main() {
	int a = 10;
	int* p = &a;
	free(p);
	p = NULL;
	return 0;
}

 我们不能对局部变量进行free释放,free函数只能用于malloc、calloc等这些存在于堆上的内存。

使用free释放一块动态开辟内存中的一部分 

#include
#include
int main()
{
	int* p = (int*)calloc(10, sizeof(int));
	if (p == NULL)
	{
		perror("calloc");
		return 1;
	}

	int i = 0;
	for (i = 0; i < 5; i++)
	{
		*p = i;
		p++;
	}
	//0 1 2 3 4 0 0 0 0 0 
	
	//释放
	free(p);
	p = NULL;

	return 0;
}

C语言——动态内存管理_第20张图片

如果我们在这个代码里,p进行++操作,当p+到5的时候,突然不想用了,那么我们是不能进行释放一部分动态开辟出来的空间的。 

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

#include
#include
int main()
{
	int* p = (int*)malloc(40);
	if (p == NULL)
	{
		//....
		return 1;
	}
	//....
	
	//释放
	free(p);
	p = NULL;
	//...

	free(p);

	return 0;
}

这里我们对p进行了两次释放,就会导致出错。

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

void test()
{
	int* p = (int*)malloc(40);
	//...
	if (3)
		return;

	free(p);
	p = NULL;
}

int main()
{
	test();
	//...
	while (1)
	{
		;
	}

	return 0;
}

这里我们在主函数中调用了test,但是进入函数malloc了空间,代码走到if时直接就返回主函数了,并没有走到free,然后主函数里面也无法对其进行释放,如果我们的程序不结束,就例如这里的while死循环,那么malloc出的空间就存在了内存泄漏。 

下次我在给大家带来一些常见的经典题目

你可能感兴趣的:(c语言,算法,开发语言,基础能力)