动态内存管理基础详解

目录

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

2、动态内存函数的介绍

2.1 malloc和free

功能:

参数和返回值:

注意事项:

tip:

2.2 calloc

 2.3 realloc函数

功能:

参数和返回值:

realloc开辟空间的两种情况

realloc会顺序采取以下措施

疑问:那realloc的返回值用原有的指针接受吗?

3、常见的动态内存错误

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

3.2、对动态开辟空间的越界访问

3.3、对非动态开辟的内存进行free释放

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

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

3.6、动态开辟内存忘记释放(内存泄露)开辟空间一定要释放


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

我们已经掌握的内存开辟方式有:

int a = 0;
char b[10] = {0};

但是上述的开辟空间的方式有两个特点:

  1. 空间开辟的大小是固定的。
  2. 数组在申明时,必须指定数组的长度,它所需要的内存在编译时分配。

但是对于空间的需求,不仅仅是上述的情况。有时我们需要的空间大小在程序运行的时候才知道

那数组的编译时开辟空间的方式就不能满足了。

这时候就只能采用动态内存开辟。

2、动态内存函数的介绍

2.1 malloc和free

C语言提供了一个动态内存开辟的函数——malloc

功能:

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

参数和返回值:

参数是以字节为单位的一个无符号整型,返回值是void*,指向我们开辟这块空间的指针。

注意事项:
  • 如果开辟成功,则返回一个指向开辟好空间的指针。
  • 如果开辟失败,则返回一个NULL指针,因此malloc函数的返回值必须要做检查
  • 返回值的类型是void*,所以malloc函数并不知道开辟空间的类型,具体在使用的时候由使用者自己决定。
  • 如果参数size是0,malloc的行为是标准未定义的。
tip:
  • malloc申请到空间后,直接返回这块空间的起始地址不会初始化空间的内容。
  • malloc申请的内存空间,当程序退出时,还给操作系统,当程序不退出,动态生气的内存是不会主动释放的,需要用free函数来释放。
  • 每当free函数释放空间后,指针就没有被指向的内容,为了防止野指针,需要将其设为控制在(即每次使用free函数后,都需要置成空指针
  • free函数不能释放非动态开辟的内存空间,这种行为是标准未定义的
  • 如果free函数的参数是空指针,则free函数什么都不做
int main()
{
	//int arr[10];
	int* p = (int*)malloc(40);
	if (p == NULL)
	{
		perror("malloc");
		return 1;
	}
	//开辟成功
	for (int i=0;i<10;i++)
	{
		printf("%d\n", *(p + 1));
	}
    free(p);
    p=NULL;
	return 0;
}

2.2 calloc

C语言还提供了一个函数是calloc,calloc函数也用来动态内存分配,原型如下:

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

举个例子:

 2.3 realloc函数

功能:

realloc函数的出现让动态内存管理更加灵活,调整对动态内存的大小。

参数和返回值:

ptr是要调整的内存地址,size是调整之后的新大小(以字节为单位),返回值为调整之后的内存起始位置。如果ptr为空指针,那么函数的功能与malloc函数一样。

realloc开辟空间的两种情况

1、原始空间的后面空间足够

直接返回原始空间的起始地址

2、原始空间的后面空间不够

realloc会顺序采取以下措施

(1)、开辟新的空间

(2)、会将旧的空间中的数据拷贝到新的空间

(3)、释放旧的空间

(4)、返回新空间的起始地址

疑问:那realloc的返回值用原有的指针接受吗?

回答:不能,因为假如realloc增容失败,会返回空指针,而原有的指针指向的空间还没有释放,就被置成空指针,这就造成了内存泄露!因此我们应该用一个新的指针去接受,然后判断增容是否成功,如果成功,就将新指针的内容赋给原有的指针,否则程序结束。

int main()
{
	int* p = (int*)malloc(40);
	if (p == NULL)
	{
		perror(malloc);
		return 1;
	}
	for (int i=0;i<2;i++)
	{
		*p = i + 1;
		p++;
		//p[i]=i+1;
	}
	int* ret = (int*)realloc(p, 80);
	if (ret == NULL)
	{
		perror(realloc);
		return 1;
	}
	p = ret;
	for (int i=0;i<20;i++)
	{
		printf("%d\n", p[i]);
	}
	free(p);
	p = NULL;
	return 0;
}

 上面是正确的使用realloc增容的方式。如果减容,直接size输入一个小于原来的值即可。

3、常见的动态内存错误

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

解决方法:判断内存开辟函数malloc/calloc/realloc返回值是否为空

3.2、对动态开辟空间的越界访问

3.3、对非动态开辟的内存进行free释放

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

 注意此时的p指向的不是我们动态开辟空间的起始地址,而是后面相当于动态内存的一部分,此时再用free释放是不对的,free释放应该从动态内存的起始地址开始释放

不能让起始指针跑偏! 

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

3.6、动态开辟内存忘记释放(内存泄露)开辟空间一定要释放

解决方法:

动态内存申请的空间不会出了作用域自动销毁,只有两种方式销毁(将内存还给操作系统):退出程序和free函数。

malloc和free函数成对使用

猜你喜欢

转载自blog.csdn.net/hanwangyyds/article/details/131839879