动态内存管理详解(malloc、calloc、realloc)

目录

一、为什么存在动态内存分配

二、动态内存函数的介绍

1.malloc

 2.calloc

 3.realloc

三、常见的动态内存的错误

1.对NULL指针进行解引用操作

2.对动态内存开辟的空间越界访问

3.对非动态内存开辟的空间使用free释放

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

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

6.动态开辟的内存未释放(内存泄漏)


一、为什么存在动态内存分配

    动态内存分配,顾名思义,就是可以对开辟的空间进行大小上的调整。

    我们已经掌握的内存分配方式有如下两种

int a = 10;//在栈区开辟4字节的整型空间

char arr[10] = {0};//在栈区开辟10字节的连续的空间

那么为什么还要进行动态内存分配呢?

原因是以上开辟的内存空间都是固定的,有可能无法达到需求。比如,在实现一个通讯录系统中,利用数组开辟100块空间来存放联系人,如果用户只需要存3个人信息,那么剩下的97块内存空间就会大大浪费,如果用户需要存储多余100人的信息,那么内存空间就会不够,这带来极大的不便。动态内存开辟极好的解决了这一问题,根据我们的需求开辟空间。

二、动态内存函数的介绍

1.malloc

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

使用malloc函数需注意几点

1.如果开辟成功 ,会返回指向这块空间的指针

2.如果开辟失败,会返回空指针(NULL),因此开辟后要检查是否开辟成功

3.该函数的返回值是void*,具体类型需要使用者根据实际情况自己决定

4.如果出现参数为0的情况,malloc的行为是标准未定义的,取决于编译器

非常重要的一点是

使用malloc函数向堆区申请了空间后,用完了要用free这个函数释放,否则会发生内存泄露。

下面举例说明

#include <stdio.h>
#include <string.h>
#include <errno.h>
#include <stdlib.h>
int main()
{
	
	//int a = 10;//4个字节
	//int arr[10];//40个字节
	//这两种方式空间开辟的大小有自身的局限性

	
	//动态内存开辟
	int* p = (int*)malloc(40);
    //判断内存空间是否开辟成功
	if (p == NULL)
	{
		printf("%s\n", strerror(errno));
		return 1;//返回其它数字不正常返回
	}
	//使用
	int i = 0;
	for (i = 0; i < 10; i++)
	{
		*(p + i) = i;
	}
	//打印
	for (i = 0; i < 10; i++)
	{
		printf("%d ", *(p + i));
	}

	//没有free
	//并不是说内存空间就不回收了
	// 当程序退出的时候,系统会自动回收内存空间的

	//内存泄露
	//一块内存在堆区被开辟,在程序运行时一直没有释放,那么这块空间只能被占有
	//别人是用不了,在一个程序中,如果一直开辟开辟空间不释放,那么内存会被填满


	free(p);//释放开辟的空间
	p = NULL;//如果p不置空,p仍然记得原来的地址,还是可以找到那块地址
	//但那块地址已经没有内容了,防止p成为野指针,所以必须将p置空

	return 0;//返回0表示正常返回
}

 2.calloc

     与malloc功能类似的还有calloc,其中,与malloc不同的地方是,calloc在开辟空间的同时还会

将这块内存空间全部初始化。

#include <stdio.h>
#include <string.h>
#include <errno.h>
#include <stdlib.h>
int main()
{
	//calloc的用途与malloc基本一致
	int* p = (int*)calloc(10, sizeof(int));
	//calloc函数在开辟空间的时候会把内容初始化成0
	//这是calloc函数的固定功能
	if (p == NULL)
	{
		printf("%s\n", strerror(errno));
        return 1;
	}
	int i = 0;
	//for (i = 0; i < 10; i++)
	//{
	//	*(p + i) = i;
	//}
	for (i = 0; i < 10; i++)
	{
		printf("%d ", *(p + i));
	}

	//释放
	free(p);
	p = NULL;

	return 0;
}

 3.realloc

   动态内存的动态就表现在reallloc这个函数上。

   有时候我们会发现申请的空间太小了,或者申请的空间太大了,这时我们可以利用realloc函数对内存进行合理的调整。

     realloc函数在调整内存空间时会出现两种情况

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

情况1
当是情况1 的时候,原有空间之后没有足够多的空间时,扩展的方法是:在堆空间上另找一个合适大小的连续空间来使用。这样函数返回的是一个新的内存地址 。

情况2
当是情况2 的时候,要扩展内存就直接原有内存之后直接追加空间,原来空间的数据不发生变化。

 所以我们使用realloc函数时需要注意一些。

#include <stdio.h>
#include <string.h>
#include <errno.h>
#include <stdlib.h>
int main()
{
	int* p = (int*)malloc(40);
	if (p == NULL)
	{
		printf("%s\n", strerror(errno));
		return 1;
	}

	//使用
	int i = 0;
	for (i = 0; i < 10; i++)
	{
		*(p + i) = i + 1;
	}
	//扩容
	
	int* ptr = (int*)realloc(p, 80);
    //realloc函数不能直接用p来接收  why?
	//开辟空间太大时,realloc函数开辟空间可能失败
	//开辟空间失败后返回NULL指针,直接用p来接受,会让p忘记之前的地址
	if (ptr != NULL)
	{
		p = ptr;
	}

	//使用
	for (i = 0; i < 10; i++)
	{
		printf("%d ", *(p + i));
	}

	//释放
	free(p);
	p = NULL;

	return 0;
}

三、常见的动态内存的错误

1.对NULL指针进行解引用操作

以上解引用操作时是错误的,会被警告。

2.对动态内存开辟的空间越界访问

//2.对动态开辟的空间越界访问
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
int main()
{
	int* p = (int*)malloc(40);
	if (p == NULL)
	{
		printf("%s\n", strerror(errno));
		return 1;
	}
	int i = 0;
	for (i = 0; i <= 10; i++)//越界访问
	{
		p[i] = i;
	}
	free(p);
	p = NULL;

	return 0;
}

 

这种非法操作会出现以上情况。

3.对非动态内存开辟的空间使用free释放

//3.对非动态开辟的内存使用free释放
int main()
{
	int a = 10;
	int* p = &a;
	//......

	free(p);
	p = NULL;

	return 0;
}

注:头文件没有添加

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

//4.使用free释放动态内存开辟的一部分
int main()
{
	int* p = (int*)malloc(40);
	if (p == NULL)
	{
		return 1;
	}
	//使用
	int i = 0;
	for (i = 0; i < 10; i++)
	{
		*p = i;
		p++;
	}
	//释放
	free(p);
	p = NULL;

	return 0;
}

注:头文件没有添加

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

//5.对同一块动态内存多次释放
int main()
{
	int* p = (int*)malloc(40);
	//...
	free(p);
  p=NULL;
	//...
	free(p);
	return 0;
}

注:头文件没有添加

6.动态开辟的内存未释放(内存泄漏)


//6.动态内存开辟忘记释放(内存泄露)
void test()
{
	int* p = (int*)malloc(100);
	
	//...
	//有可能有些人这么写代码
	int flag = 0;
	scanf("%d", &flag);
	if (flag == 5)
		return;

	//如果返回以下代码没有机会执行
    //开辟的内存空间在程序执行的时候再也找不到了
	free(p);
	p = NULL;
}
int main()
{
	test();
	return 0;
}

注:头文件没有添加

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值