C语言——动态内存管理


既然选择远方,当不负青春,砥砺前行

一、 Why要有动态内存的开辟?

在此之前我们已经学会了静态的内存开辟。

#include<stdio.h>
int main()
{

	int a = 10;
	int arr[10] = { 1,2,3,4,5,6 };

	return 0;
}
  • 但是静态开辟的内存空间大小是固定的。
  • 对于数组,一旦长度确定后,就不能在改变了。

但是在实际的开发中,我们需要的空间事先一开始并不知道,而是在程序运行的时候才知道。 所以引入了动态内存的开辟,让我们自己申请和释放空间。
那么在自己申请和释放空间时就一定会用到一下四个函数,掌握了以下四个函数的使用,也就会自己进行动态内存的管理了!!!

简单讨论一下分区

开辟动态内存是在堆区开辟,使用下面四个函数要包含头文件 #include<stdlib.h>
在这里插入图片描述

  • 栈区:先进后出,程序一开始先从main()进入,main()入栈,为局部变量,函数调用,形式参数,返回值在栈上分配空间,再调用各种函数入栈,出栈。最后mian()出栈,程序执行完毕。栈是有大小的,无限次递归会造成栈溢出。

  • 堆区:自己动态地向内存分配空间的区域。malloc calloc realloc 动态开辟内存函数,free 释放动态内存开辟的函数。如果忘记释放动态分配的内存空间可能会导致内存泄露。

  • 静态区:存放静态变量和全局变量的区域,这些变量的作用域为整个main()函数,生命周期和程序的生命周期一样,只有程序结束,这些变量才会被回收。

二、malloc

C语⾔动态内存开辟的函数之一:

//声明:
 void* malloc (size_t size)
 
  • 返回值类型是void * 自己想开辟啥类型的空间,可以自己强转。
  • 参数 size 为所要开辟空间的字节数,如果传入的size=0,一般编译器啥也不会报错。
  • 如果开辟成功则返回一个指向开辟好空间的指针
  • 如果开辟失败则返回一个NULL指针,所以再开辟空间后一定要检查指针是否为NULL

使用:


#define  _CRT_SECURE_NO_WARNINGS 1
#include<stdio.h>
#include<stdlib.h>


int main()
{
	
	int* p = (int* )malloc(4*5); //动态开辟5个整形的空间
	if (p == NULL) //检查指针
	{
		perror("malloc"); //打印错误信息
	}
	//使用
	for (int i = 0; i < 5; i++)
	{
		p[i] = i + 1;
	}

	for (int i = 0; i < 5; i++)
	{
		printf("%d ", p[i]);
	}

	return 0;
}
  • 内存
    在这里插入图片描述
  • 输出
    在这里插入图片描述

三、free

C语言专门用来做——动态内存的释放和回收。

void free (void* ptr)
  • 参数ptr指向的空间一定要是动态开辟的,否则报错。ptr 一定指向空间的起始地址
  • 如果ptr是NULL,则啥也不干。

#include<stdio.h>
#include<stdlib.h>


int main()
{
	
	int* p = (int* )malloc(4*5); //动态开辟5个整形的空间
	if (p == NULL)
	{
		perror("malloc");
	}
	//使用
	for (int i = 0; i < 5; i++)
	{
		p[i] = i + 1;
	}

	for (int i = 0; i < 5; i++)
	{
		printf("%d ", p[i]);
	}

	
	free(p);//释放p所指向的动态内存
	p = NULL; 
	//既然释放了,p指向的内存空间也已经被回收了。
	//所以p置为NULL更安全

	return 0;
}
  • 释放的空间不是动态开辟的,报错
    在这里插入图片描述

四、calloc

calloc 函数也⽤来动态内存分配,那和malloc 有什么不同呢?

 void* calloc (size_t num, size_t size) 
  • 参数 num 为开辟空间类型的个数
  • 参数 size 未开辟空间类型所占字节的大小
  • 与函数 malloc 的区别只在于 calloc 会把申请的空间的每个字节初始化为0

#include<stdio.h>
#include<stdlib.h>


int main()
{
   
   int* p = (int* )calloc(5,sizeof(int)); //动态开辟5个整形的空间
   if (p == NULL)
   {
   	perror("calloc");
   	return -1;
   }
   //输出
   for (int i = 0; i < 5; i++)
   {
   	printf("%d ", p[i]);
   }

   
   free(p);
   p = NULL; 


   return 0;
}

在这里插入图片描述
如果我们所要开辟的空间要进行初始化,可以使用calloc。

五、realloc

realloc函数的出现则让动态内存管理更加灵活!!!
虽然我们可以动态地开辟内存,但是难免会出现,申请的空间过大或者过小的情况。于是realloc 可以再次对动态开辟的空间进行调整。

void* realloc (void* ptr, size_t size)
  • 参数ptr 指向要调整的内存,ptr 一定指向空间的起始地址
  • 参数size 为调整后,新的空间所占字节数
  • 返回新开辟的空间地址

这个函数是怎么调整的呢?
在原有内存的基础上,将其拷贝一份到新调整的空间,释放原来的空间,返回新的空间起始地址!

有如下三种情形:

1.原有空间后面有足够大的空间,容得下调整之后的空间大小。
2.原有空间后面不足够大的空间,容不下调整之后的空间大小。
3.调整空间失败。

  • 1.原有空间后面有足够大的空间
- #define  _CRT_SECURE_NO_WARNINGS 1
#include<stdio.h>
#include<stdlib.h>


int main()
{
   
   int* p = (int* )calloc(5,sizeof(int)); //动态开辟5个整形的空间
   if (p == NULL)
   {
   	perror("calloc");
   }
   //调整p指向的空间,从原来5个int --> 10个int
   int* tmp = (int*)realloc(p, sizeof(int) * 10);
   if (tmp == NULL) 
   {
   	perror("realloc");
   	return -1;
   }

   p = tmp;

   //使用
   for (int i = 0; i < 10; i++)
   {
   	p[i] = i + 1;
   }
   //打印
   for (int i = 0; i < 10; i++)
   {
   	printf("%d ", p[i]);
   }

   free(p);
   p = NULL; 

   return 0;
}

在这里插入图片描述

  • 2 原有空间后面不足够大的空间
#define  _CRT_SECURE_NO_WARNINGS 1
#include<stdio.h>
#include<stdlib.h>


int main()
{
	
	int* p = (int* )calloc(5,sizeof(int)); //动态开辟5个整形的空间
	if (p == NULL)
	{
		perror("calloc");
	}
	//调整p指向的空间,从原来5个int --> 20个int

	int* tmp = (int*)realloc(p, sizeof(int) * 20);
	if (tmp == NULL) 
	{
		perror("realloc");
		return -1;
	}

	p = tmp;

	//使用
	for (int i = 0; i < 20; i++)
	{
		p[i] = i + 1;
	}
	//打印
	for (int i = 0; i < 20; i++)
	{
		printf("%d ", p[i]);
	}

	free(p);
	p = NULL; 

	return 0;
}

在这里插入图片描述

  • 3 调整的空间太大了,调整空间失败。
#define  _CRT_SECURE_NO_WARNINGS 1
#include<stdio.h>
#include<stdlib.h>


int main()
{
	
	int* p = (int* )calloc(5,sizeof(int)); //动态开辟5个整形的空间
	if (p == NULL)
	{
		perror("calloc");
	}
	//调整p指向的空间,从原来5个int --> #define LLONG_MAX  9223372036854775807i64

	int* tmp = (int*)realloc(p, sizeof(int) * LLONG_MAX);
	if (tmp == NULL) 
	{
		perror("realloc");
		return -1;
	}

	p = tmp;

	//使用
	for (int i = 0; i < 20; i++)
	{
		p[i] = i + 1;
	}
	//打印
	for (int i = 0; i < 20; i++)
	{
		printf("%d ", p[i]);
	}

	free(p);
	p = NULL; 

	return 0;
}

在这里插入图片描述

六、常见的6大动态内存错误

1.对NULL指针的解引用操作

#define  _CRT_SECURE_NO_WARNINGS 1
#include<stdio.h>
#include<stdlib.h>

void test()
{

	int* p = (int*)malloc(INT_MAX);
	*p = 20;//如果p的值是NULL,就会有问题
	free(p);
}

int main()
{
	test();

	return 0;
}

2.对非动态内存的空间进行释放

#define  _CRT_SECURE_NO_WARNINGS 1
#include<stdio.h>
#include<stdlib.h>

void test()
{
	int a = 6;
	int* p = &a;//对非动态内存的空间进行释放
	free(p);
}

int main()
{
	test();

	return 0;
}

3.对动态开辟的空间进行越界访问

#define  _CRT_SECURE_NO_WARNINGS 1
#include<stdio.h>
#include<stdlib.h>

void test()
{
	
	int* p = (int*)calloc(10,sizeof(int));
	for (int i = 0; i <= 10; i++)
	{
		printf("%d ", p[i]); //对动态开辟的空间进行越界访问
	}

	free(p);
}

int main()
{
	test();

	return 0;
}

4.动态开辟空间后,未释放,造成空间泄露

#define  _CRT_SECURE_NO_WARNINGS 1
#include<stdio.h>
#include<stdlib.h>

void test()
{
	
	int* p = (int*)calloc(10,sizeof(int));
	for (int i = 0; i < 10; i++)
	{
		printf("%d ", p[i]);
	}
	//动态开辟空间后,未释放,造成空间泄露
	
}

int main()
{
	test();

	return 0;
}

5.对一块动态开辟的空间进行多次释放

#define  _CRT_SECURE_NO_WARNINGS 1
#include<stdio.h>
#include<stdlib.h>

void test()
{
	
	int* p = (int*)calloc(10,sizeof(int));
	for (int i = 0; i < 10; i++)
	{
		printf("%d ", p[i]);
	}

	free(p);
	free(p);//对一块动态开辟的空间进行多次释放
}

int main()
{
	test();

	return 0;
}

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

#define  _CRT_SECURE_NO_WARNINGS 1
#include<stdio.h>
#include<stdlib.h>

void test()
{
	
	int* p = (int*)calloc(10,sizeof(int));
	p++; //使用free释放一块动态开辟内存的一部分
	free(p);
}

int main()
{
	test();

	return 0;
}

七、经典笔试4道题分析

1.笔试题一

#define  _CRT_SECURE_NO_WARNINGS 1
#include<stdio.h>
#include<stdlib.h>

void GetMemory(char* p)
{
	p = (char*)malloc(100);
}

void test(void)
{
	char* str = NULL;
	GetMemory(str);
	strcpy(str, "hello world");
	printf(str);
}

int main()
{
	test();

	return 0;
}

在这里插入图片描述
2.笔试题二

#define  _CRT_SECURE_NO_WARNINGS 1
#include<stdio.h>
#include<stdlib.h>

char* GetMemory(void)
{
	char p[] = "hello world";
	return p;
}
void test(void)
{
	char* str = NULL;
	str = GetMemory();
	printf(str);
}

int main()
{
	test();

	return 0;
}

在这里插入图片描述

3.笔试题三

#define  _CRT_SECURE_NO_WARNINGS 1
#include<stdio.h>
#include<stdlib.h>

void GetMemory(char** p, int num)
{
	*p = (char*)malloc(num);
}
void test(void)
{
	char* str = NULL;
	GetMemory(&str, 100);
	strcpy(str, "hello");
	printf(str); //就是用完要及时free
}

int main()
{
	test();

	return 0;
}

在这里插入图片描述

4.笔试题四

#define  _CRT_SECURE_NO_WARNINGS 1
#include<stdio.h>
#include<stdlib.h>

void test(void)
{
	char* str = (char*)malloc(100);
	strcpy(str, "hello");
	free(str);
	if (str != NULL)
	{
		strcpy(str, "world"); 
		printf(str);
	}
}


int main()
{
	test();

	return 0;
}

在这里插入图片描述

  • 28
    点赞
  • 21
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 2
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

我是无敌小恐龙

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值