栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > C/C++/C#

(C语言)浅谈——动态内存管理

C/C++/C# 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

(C语言)浅谈——动态内存管理

目录

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

二、动态内存函数的介绍

1、malloc

2、free

3、calloc

4、realloc

三、常见的动态内存错误

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

2、对动态开辟的内存的越界访问

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

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

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


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

看以下代码:

int a = 10;
int arr[10] = { 0 };

        第一个是向空间申请了4个字节,第二个是向空间申请了40个字节。

这样开辟空间的方式有2个特点:

1、空间开辟大小是固定的

2、数组在申明的时候,必须指定数组的长度,它所需要的内存在编译时分配

        想一想,我们在写课设作业的时候,比如写了一个音乐管理系统,对音乐信息的增删查改,那我们需要给音乐信息开辟空间,用数组的方式开辟了100个空间,那如果系统要存放200首音乐信息,空间就不够用,如果只需要存放50首歌曲信息,我们开辟的100首的信息空间就浪费了。对于空间的需求,有时候我们需要的空间大小在程序运行时侯才能知道,那数组在编译时开辟的空间的方式就不能满足了,所以在这里,我们需要一种更加灵活的开辟空间的方法——动态内存管理。 

二、动态内存函数的介绍

在msdn里可以查找以下函数的定义及使用

以下函数在使用时,需要包含一个头文件

#include 

1、malloc

 函数解释:

        这个函数可以帮我们向内存申请一块连续可用的空间,并且返回指向这块空间的指针

使用注意:

1、如果开辟成功,返回指向开辟好的空间的地址

2、如果开辟失败,返回一个NULL指针,因此malloc的返回值需要判断是否为空指针后再使用

3、返回值是void*,在使用时需要我们自己来决定开辟空间的类型

注:为什么是void*,因为这个函数不知道申请者需要什么什么类型,它对开辟后的空间的用途是未知的,所以返回void*,具体什么用途什么类型都得看申请者自己

4、如果参数size为0,malloc的行为是标准未定义的,取决于编译器

下面看一个例子:

#include
#include
int main()
{
	int* p = (int*)malloc(40);
	if (p == NULL)
	{
		printf("开辟失败n");
	}
	else
	{
		int i = 0;
		for (i = 0; i < 10; i++)
		{
			*(p + i) = i;
			printf("%d ", p[i]);
		}
		printf("n");
	}
	return 0;

}

代码分析:

1、malloc开辟的是字节数,返回的是开辟的空间的指针(void*),而我们现在需要的是int的数据,所以需要 先强制类型转换为int*的指针,然后再用指针变量p来接收。

2、判断是否开辟成功,检查p是否为空指针

3、开辟成功,开始赋值,*(p+i)与p[i]是等价的

看结果:

补充一点小知识:

动态内存管理的函数开辟的内存都是在堆区上开辟的,关于堆区,浅浅普及一下:

堆区的内存空间由程序员分配和释放,若程序员不释放,程序结束时由操作系统回收

大家会注意到,它分配的空间是由程序员自己释放的,所以就需要认识下一个函数

free

2、free

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

 

参数:内存空间的指针 

 free用来释放动态开辟的内存

1、如果参数memblock指向的空间不是动态开辟的,那free函数的行为是未定义的

2、如果参数memblock是NULL,则函数什么都不用做

如果不使用free,就可能会造成内存泄漏

关于内存泄漏,不深入去说了,但可以观察一个现象,看如下代码:

#include
#include
int main()
{
	while (1)
	{
		malloc(10);
	}
	return 0;
}

        有兴趣的可以自己去试试。但是,轻易别尝试,对电脑不好,本博主的电脑就是准备试一下给你们截个图看看,电脑它不争气,卡死重启了,真的会难过。

        如果有小伙伴想尝试,可以去运行,不关闭程序,然后去电脑的任务管理器中去查看内存,会发现内存会越来越少,最后的结果就是,电脑就卡死了。 

        有人会说,程序结束他自己不就释放了吗?再说了,上面的malloc的使用举例中,不就是没有使用free,也没有发现电脑有卡死的现象啊!

        兄弟,想的很对,但是,有没有想过,刚才的程序运行很快结束了,万一我们有一个很大的项目,它运行起来,可能需要每天24小时不间断的运行,或者是想上述一样是个死循环,这时候是不是就有问题了,申请的内存空间就得不到释放,就很可能会造成内存泄露的,所以我们需要养成习惯,记得自己去释放它。

所以在上述代码在结尾加上free

        问题又来了,我们将它free了之后,将相应的空间还给了操作系统,但是我们通过监视中观察到以下情况:

free之前的其存放的值:

继续向下看,free之后的值

 看似是没有问题了,但是再看:

 有没有发现p所指向的空间编号并没有发生改变,也就是说通过

        我们还可以通过p找到那块已经不属于我们的空间,就是一个野指针了,关于野指针,可以看一看我的另一期,讲解更加详细。http://t.csdn.cn/HICut 

所以我们不仅要free还要将其置为NULL.

3、calloc

用来动态内存分配空间

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

        2、与函数malloc的 区 别是,calloc会返回地址之前把申请的空间的每个字节初始化为全0

使用:

#include
int main()
{
	//malloc(10*sizeof(int));//效率高,但不初始化
	int* p=(int*)calloc(10, sizeof(int));//效率低一点点,但会自动初始化
	if (p == NULL)
	{
		printf("开辟失败");
	}
	else
	{
		int i = 0;
		for (i = 0; i < 10; i++)
		{
			printf("%d ", *(p + i));//0 0 0 0 0 0 0 0 0 0
		}
	}
	//释放空间
	free(p);
	p = NULL;
	return 0;
}

4、realloc

调整动态开辟内存空间的大小 

realloc使用的注意事项:
    1、如果p指向的空间之后有足够的内存空间可以追加,则直接追加,后返回p
    2、如果p指向的空间之后没有足够的内存空间可以追加,则realloc会重新找一块内存区域,开辟一块满足需求的空间,并且把原来内存中的数据拷贝过来,释放旧的内存空间,最后返回新开辟的内存空间的地址
    3、得用一个新的变量接收realloc函数的返回值

 同时呢,为了防止内存空间不足,开辟失败,所以。利用realloc开辟空间的形式为:

    int* p = (int*)malloc(20);
	if (p == NULL)
	{
		printf("strerror(errno");
		return;
	}
	int* ptr = realloc(p, INT_MAX);
	if (ptr != NULL)
	{
		p = ptr;
	}

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

        使用realloc要先将其开辟的空间赋给ptr,开辟失败,ptr为空,但是并不会影响原来的p,否则,开辟失败,直接赋给p,不仅没有调整成功,反而还把原来的p也变成NULL了。

        如果开辟成功,再将ptr赋给p。

三、常见的动态内存错误

1、对NULL指针的解引用操作
#include
#include
int main()
{
	int* p = (int*)malloc(INT_MAX+1);
	//如果p的值为NULL,就会有问题
	int i = 0;
	for (i = 0; i < 10; i++)
	{
		*(p + i) = i;//对非法地址进行了解引用
	}
	free(p);
	p = NULL;
	free(p);
}

        INT_MAX+1开辟的空间太大了,就会开辟失败,返回一个NULL,而我们又没有去判断,直接拿来用,就会出现非法访问了。运行起来,就会出错。

2、对动态开辟的内存的越界访问
int main()
{
	int* p = (int*)malloc(5 * sizeof(int));
	if (p == NULL)
	{
		return 0;
	}
	else
	{
		int i = 0;
		for (i = 0; i < 10; i++)//只开辟了5个,却用到了10个
		{
			*(p + i) = i;
		}
	}
	free(p);
	p = NULL;
}

越界访问,代码运行时,会出现下列提示:

3、对非动态开辟内存使用free释放
int main()
{
	int a = 10;
	int* p = &a;
	*p = 20;
	free(p);
	p = NULL;
	return 0;
}

 出错:

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

 代码崩溃了,一直给p++,最后释放的是p后面的空间,而我们要是放的是最初的p的后面的空间
p变化,p所指的就不是malloc开辟的起始位置了

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

如何避免这个情况:
每次free(p);结束将其置为NULL

以上就是动态内存管理的主要内容了,使用及其容易出错的问题,我们下期见!!! 

转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/1011371.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号