当前位置:   article > 正文

动态内存malloc,calloc,free,realloc_c语言 malloc动态内存不释放,一直占用

c语言 malloc动态内存不释放,一直占用

✨✨✨学习的道路很枯燥,希望我们能并肩走下来!

编程真是一件很奇妙的东西。你只是浅尝辄止,那么只会觉得枯燥乏味,像对待任务似的应付它。但你如果深入探索,就会发现其中的奇妙,了解许多所不知道的原理。知识的力量让你沉醉,甘愿深陷其中并发现宝藏。


前言

本篇对动态内存总结,进一步了解动态内存函数
malloc,calloc,realloc.free.如有错误,请在评论区指正,让我们一起交流,共同进步!


本文开始

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

我们已掌握的内存开辟放式:固定方式

int val = 20; //=》申请4个字节空间
char arr[10] = { 0 };// =》申请10个内存空间
  • 1
  • 2

特点:

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

对于固定的空间大小,如果我们想要扩大或者缩小空间,固定的空间就满足不了我们的需求。这时候就需要动态开辟。

2.动态内存函数的介绍

2.1 malloc

C语言提供的动态内存开辟的函数malloc
malloc: 函数向内存申请一块连续可用的空间,并返回指向这块空间的指针
在这里插入图片描述
void* :返回类型(开辟空间的起始地址返回到void*)
size_t size: 需要开辟的字节内存大小

为什么是void*
只知道开辟的总空间大小,不知道开辟空间大小的类型是什么,所以为了更通用使用void* 来维护。

int main()
{
	void* p = malloc(40);
	return 0;
}
  • 1
  • 2
  • 3
  • 4
  • 5

上述是使用malloc开辟40个字节内存空间,但是我们一般不这样使用,我们如果解引用void* 不知道访问几个字节空间,+1,-1也无法知道内存移动几个字节空间,所以我们一般指定类型如下:

 int* p = (int*)malloc(40);
 int* p1 = (int*)malloc(10*sizeof(10));
  • 1
  • 2

内存如果开辟失败返回NULL,所以开辟空间后一定要判断一下返回不为空。

但是当开辟空间过大时,会产生错误如下图:

在这里插入图片描述

动态内存空间开辟时在堆上开辟,如果不主动释放,会一直占用空间(程序不结束的时候)。

总结:

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

2.2 free

free:内存释放函数

在这里插入图片描述
void* ptr:想要释放空间的地址

注意:我们释放的是开辟空间的首地址,如果没有保存首地址,放入的是已经移动的地址,会产生释放失败的情况。(保留开辟空间的首地址

在free()空间后p指向的还是起始开辟空间的地址(不置空P为野指针),此时如果有人,再次访问这块空间会发生非法访问,所以释放完必须将指针置NULL(代表0);

开辟并释放空间代码示范:

#include<stdio.h>
#include<errno.h>
#include<string.h>
int main()
{
	//void* p = malloc(40);
	int* p = (int*)malloc(2147483647);
	//INT_MAX == 2147483647
	int* ptr = p;
	if (p == NULL)
	{
		printf("%s\n", strerror(errno));
		return 1;
	}
	//使用
	int i = 0;
	for (i = 0; i < 10; i++)
	{
		*ptr = i;
		ptr++;
	}
	//释放
	free(p); //
	p = NULL; // 置空
	return 0;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26

不释放空间,内存会一直被占用。所以我们开辟空间后,一定要记得释放空间

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

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

2.3 calloc

calloc也可以用来动态内存分配

在这里插入图片描述

num: 开辟元素的个数
size:每个元素的大小(字节)

int main()
{
	int* p = (int*)calloc(10, sizeof(int));
	if (p == NULL)
	{
		perror("calloc");
		return 1;
	}
	int i = 0;
	for (i = 0; i < 10; i++)
	{
		*(p + i) = i;
	}
	//释放
	free(p);
	p = NULL;
	return 0;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18

calloc特点:

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

2.4 realloc

realloc:改变ptr指向的空间大小为size(字节);

  1. realloc函数的出现让动态内存管理更加灵活。
  2. 有时会我们发现过去申请的空间太小了,有时候我们又会觉得申请的空间过大了,为了合理的内存,我们一定会对内存的大小做灵活的调整。那 realloc 函数就可以做到对动态开辟内存大小的调整

在这里插入图片描述

  1. ptr 是要调整的内存地址
  2. size 调整之后新大小
  3. 返回值为调整之后的内存起始位置。
  4. 这个函数调整原内存空间大小的基础上,还会将原来内存中的数据移动到新的空间。
  5. realloc在调整内存空间的是存在两种情况:
    情况1:原有空间之后有足够大的空间
    情况2:原有空间之后没有足够大的空间
int main()
{
	int* p = (int*)malloc(40);
	if (p == NULL)
	{
		return 1;
	}
	//使用
	int i = 0;
	for (i = 0; i < 10; i++)
	{
		*(p + i) = i;
	}
	for (i = 0; i < 10; i++)
	{
		printf("%d ", *(p + i));
	}
	//增加空间
	int* ptr = (int*)realloc(p, 80);
	if(ptr != NULL)
	{
		p = ptr;//要维护p,所以赋值
		ptr = NULL;//不用ptr让ptr为空
	}
	free(p);
	p = NULL;

	//
	return 0;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30

调整空间的两种情况:

在这里插入图片描述

realloc(NULL,40); == malloc(40);//realloc其中传递空指针时与malloc一样。

3.常见的动态内存错误

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

释放内存是对于动态开辟的内存

int main()
{
	//局部变量大括号内创建,出大括号销毁
	int num = 10;
	int* p = &num;
	//不需要释放
	free(p);
	p = NULL;
	return 0;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10

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

释放空间一定要记得空间起始位置

int main()
{
	int* p = (int*)malloc(40);
	if (p == NULL)
	{
		return 1;
	}
	int i = 0;
	for (i = 0; i < 5; i++)
	{
		*p = i;
		p++;
	}
	//释放
	//释放时P已经不再指向动态内存开辟空间的起始地址了,free会失败
	//释放空间一定要记得空间起始位置
	free(p);
	p = NULL;
	return 0;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20

3.3对一块动态内存多次释放

不置空,重复释放会出现错误

int main()
{
	int* p = (int*)malloc(40);
	if (p == NULL)
	{
		return 1;
	}
	int i = 0;
	for (i = 0; i < 5; i++)
	{
		*(p + i) = i;
	}
	free(p); 
	               //p = NULL;
	free(p); //如果上述p置空,此时可释放,相当于释放NULL作用不大
	return 0;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17

3.4动态开辟内存忘记释放(内存泄漏

动态开辟的空间一定要释放,并且正确释放
释放是指:ptr指向的空间
使用get_,memory()函数开辟动态内存空间,但是忘记释放,会造成内存泄漏。

int* get_memory()
{
	int* p = (int*)malloc(40);
	return p;
}

int main()
{
	int* ptr = get_memory();
	//使用ptr
	//没有释放
	return 0;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13

3.5对NULL指针的解引用操作

开辟动态内存空间,不确定是否开辟空间成功,必须if(p !=NULL)判断开辟空间不为空。

int main()
{
	int* p = (int*)malloc(40);
	*p = 20; 
	free(p);
	return 0;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

3.6 对动态开辟空间越界访问

越界访问动态内存空间

int main()
{
	int* p = (int*)malloc(40);
	
	int i = 0;
	for (i = 0; i <= 10; i++)
	{
		*(p + i) = i;
	}
	free(p);
	p = NULL;
	return 0;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13

4. 几个经典错误

通过几个经典题目再熟悉一下动态开辟出现的错误。

4.1引用空指针错误

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();
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15

注意错误:①调用完getMemory函数,str指向的为空,解引用空指针错误。
②malloc开辟的空间没有释放,存在内存泄漏。

图解:在这里插入图片描述

改进代码(传地址):

在这里插入图片描述

4.2数组空间被非法访问(返回栈空间地址问题

p存原来地址,但原有空间已经被释放,再次访问属于非法访问空间。

char* GetMemory(void)
{
	char p[] = "hello world";
	return p;
}
void Test(void)
{
	char* str = NULL;
	str = GetMemory();
	printf(str);
}
int main()
{
	Test();
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15

图示:在这里插入图片描述

4.3野指针

free() 完没有置空,指针为野指针

void Test(void)
{
	char *str = (char *) malloc(100);
	strcpy(str, "hello");
	free(str);
if(str != NULL)
{
	//str是野指针,这里是非法访问
	strcpy(str, "world");
	printf(str);
}
}
int main()
{
	Test();
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16

总结

✨✨✨各位读友,本篇分享到内容是否更好的让你理解了动态内存开辟,如果对你有帮助给个

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/盐析白兔/article/detail/969297
推荐阅读
相关标签