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

c语言动态内存管理

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

c语言动态内存管理

目录

1.为什么存在动态内存分配

2.动态内存函数的介绍

3.常见的动态内存错误


1.为什么存在动态内存分配

在了解之前我们可以先了解一下内存的几个区,如下图:

 

 

 

 

 

可以看到动态内存是在堆区上实现的,涉及动态内存分配的几个函数有malloc,calloc,realloc,free。

我们一般的内存开辟都是在栈区上的,比如一个整形变量在栈空间上开辟四个字节,一个整形数组在栈区开辟一块连续的空间,但是这种方法有两个特点:
1.空间开辟大小是固定的。

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

但是对于空间的需求,不仅仅是这些情况,有时候我们需要的空间大小在程序运行的时候才能知道,这时候就只能试试动态内存开辟了。

2.动态内存函数介绍

1.malloc和free

malloc函数:void* malloc(size_t size);

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

  • 如果开辟成功,则返回一个指向开辟好空间的指针。
  • 如果开辟失败,则返回一个NULL指针,因此malloc的返回值一定要做检查。
  • 返回值的类型是void*,所以malloc函数并不知道开辟空间的类型,具体在使用的时候使用者自己来决定。
  • 如果参数size为0malloc的行为是标准未定义的,取决于编译器。

free函数:void free(void* ptr);

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

  • 如果参数ptr指向的空间不是动态开辟的,那free函数的行为是未定义的。
  • 如果参数ptr是NULL指针,则函数什么事都不做。

malloc和free都声明在stdlib.h头文件中

举个例子:

#define _CRT_SECURE_NO_WARNINGS 1
#include
#include
int main()
{
	//申请空间
	int* p = (int*)malloc(40);//拿一个整形指针来维护后面的40个字节,向内存申请了40个字节的空间,把这个四十个字节的起始地址强制类型转换成int*之后赋值给p的指针
	if (p == NULL)
	{
		return -1;
	}
	//开辟成功了
	int i = 0;
	for (i = 0; i < 10; i++)
	{
		*(p + i) = i;
	}
	//释放空间
	free(p);//在释放时不会主动把p指针变为空,所以要主动赋值为空,这样p才不会指向malloc指向的空间
	p = NULL;
	return 0;
}

 可以看到开辟成功后地址都对应i的值发生了改变。

2.calloc

calloc函数:void* calloc(size_t num,size_t size);

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

举个例子:

int main()
{
	int* p = (int*)calloc(10, sizeof(int));
	if (p == NULL)
	{
		printf("%sn", strerror(errno));//开辟失败时报错,打印错误的信息
		return -1;
	}
	//申请成功
	int i;
	for (i = 0; i < 10; i++)
	{
		printf("%d ", *(p + i));//打印10个0
	}
	//释放空间
	free(p);
	p = NULL;
	return 0;
}

 可以看到开辟成功时,打印了十个0,确实是把空间的每个字节初始化为0.

 当我们申请空间过大开辟失败时会返回错误信息。

3.realloc

realloc函数:void* realloc(void* ptr,size_t size);

  • realloc函数的出现让动态内存管理更加灵活。
  • 有时候我们会发现过去申请的空间太小了或者太大了,为了合理使用内存,我们就可以使用realloc函数对动态开辟内存的大小进行调整。

realloc参数的解释:

  • ptr是要调整的内存地址
  • size调整之后新大小
  • 返回值为调整之后的内存起始位置
  • 这个函数调整原内存空间大小的基础上,还会将原来内存中的数据移动到新的空间。
  • 开辟失败返回空指针。
  • realloc在调整内存空间的是存在两种情况:

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

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

如下图所示,直接可以在后面追加空间

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

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

如下图所示:

代码如下:

 

int main()
{
	//申请10个int的空间
	int* p = (int*)calloc(10, sizeof(int));
	if (p == NULL)
	{
		printf("%sn", strerror(errno));//开辟失败时报错,打印错误的信息
		return -1;
	}
	//申请成功
	int i;
	for (i = 0; i < 10; i++)
	{
		*(p + i) = i;
	}
	//空间不多了,增加空间至20个int
	int* ptr = (int*)realloc(p, 20 * sizeof(int));//定义新的指针接收地址
	if (ptr != NULL)//由于开辟失败会返回空指针
	{
		p = ptr;//若开辟成功将ptr的值赋给p
	}
	else
	{
		return -1;
	}
	for (i = 10; i < 20; i++)
	{
		*(p + i) = i;
	}
	//打印20个,结果为0到19
	for (i = 0; i < 20; i++)
	{
		printf("%d ", *(p + i));
	}
	//释放空间
	free(p);
	p = NULL;
	return 0;
}

3.常见的动态内存错误 

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

int main()
{
	int* p = (int*)malloc(20);
	//错误操作
	
	//正确操作,对malloc的返回值进行一个判断,然后再进行赋值
	if (p == NULL)
	{
		return -1;
	}
	*p = 0;
	return 0;
}

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

int main()
{
	int* p = (int*)malloc(200);
	if (p == NULL)
	{
		return -1;
	}
	//使用
	int i = 0;
	//越界访问,申请的空间应该只有50个整形数据,会产生错误
	for (i = 0; i < 80; i++)
	{
		*(p + i) = i;
	}
	//释放
	free(p);
	p = NULL;
	return 0;
}

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

int main()
{
	int i = 0;
	int* p = &i;
	//p指向的空间在栈上,此时用free释放p的空间就会产生错误,释放了一个非堆上的空间
	free(p);
	return 0;
}

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

int main()
{
	int* p = (int*)malloc(10 * sizeof(int));
	if (p == NULL)
	{
		return -1;
	}
	//使用
	int i = 0;
	for (i = 0; i < 10; i++)
	{
		*p++ = i;//p在++之后地址已经改变,起始位置已经不在开头了,此时进行释放空间就会产生错误
	}
	//释放
	free(p);
	p = NULL;
	return 0;
}

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

int main()
{
	int* p = (int*)malloc(40);
	if (p == NULL)
	{
		return -1;
	}
	//释放
	//两次释放时错误的
	//正确写法
	free(p);
	p = NULL;//将p变为空指针
	free(p);//释放空指针是不会产生变化的
	p = NULL;
}

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

int main()
{
	int* p = (int*)malloc(40);
	if (p == NULL)
	{
		return -1;
	}
	//使用
	//忘记释放了
	getchar();//当程序不释放也不结束的时候,程序一直在运行,如果一个程序一直在开辟空间却不释放也没有结束程序,就会容易造成内存泄漏
	return 0;
}

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

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

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