动态内存管理

为什么存在动态内存分配

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

int val = 20;//在栈空间上开辟4个字节
char arr[10] = {0};//在栈空间上开辟10个字节的连续空间

但是上述的开辟空间的方式有两个特点:
1、空间开辟大小是固定的;
2、数组在声明的时候,必须指定数组的长度,它所需要的内存在编译时分配。

    /*
	int n = 1;
	cin >> n;
	const int len = n;
	char array[len] = {0};//非法
	*/
	const int len = 4;//在C++中,len是常量。
	char array[len] = {0};//合法,因为数组所需要的内存在编译时分配。

还有val局部变量、全局变量、static变量也都是在编译时分配内存。
sizeof的大小也是在编译时确定的
但是对于空间的需求,不仅仅是上述的情况。有时候我们需要的空间大小在程序运行的时候才能知道(这也就是为什么叫做动态内存分配了),那数组的编译时开辟空间的方式就不能满足了。
这时候就只能试试动态内存开辟了。

动态内存函数的介绍

malloc和free
C语言提供了一个动态内存开辟的函数
void * malloc(size_t size);
malloc函数向内存申请⼀块连续可⽤的空间,并返回指向这块空间的指针。
如果开辟成功,则返回⼀个指向开辟好空间的指针,并且空间中的内容是为初始化的
如果开辟失败,则返回⼀个NULL指针,因此malloc的返回值⼀定要做检查。
返回值的类型是 void* ,所以malloc函数并不知道开辟空间的类型,具体在使⽤的时候使⽤者
⾃⼰来决定(强转)。
如果参数 size 为0,malloc的⾏为在标准中是未定义的,取决于编译器。

C语言提供了另外一个函数free,是专门用来做动态内存的释放的。
void free(void * ptr);
如果参数 ptr 指向的空间不是动态开辟的,那free函数的⾏为是未定义的。
如果,free(NULL);//此时free函数不执行任何操作
注意:
free函数不会更改ptr本身的值,因此ptr仍指向原位置,只不过现在是无效的。

	int num = 0;
	scanf("%d", &num);
	int *ptr = NULL:
	ptr = (int *)malloc(num * sizeof(int));
	if(NULL != ptr)//判断内存是否开辟成功
	{
		for(int i = 0; i < num; ++i)
			*(ptr+i) = 0;
	}
	free(ptr);//手动释放
	ptr = NULL;//这个操作是有必要的

calloc
C语⾔还提供了⼀个函数叫 calloc , calloc 函数也⽤来动态内存分配。
void * calloc(size_t num, size_t size);
calloc函数的功能是为 num 个⼤⼩为 size 的元素开辟⼀块空间,并且把空间的每个字节初始化为0。
与函数 malloc 的区别只在于 calloc 会在返回地址之前把申请的空间的每个字节初始化为全0。

	int *p = (int *)calloc(10, sizeof(int));
 	if(NULL != p)
 	{
 	//使⽤空间
 	}
 	free(p);
 	p = NULL

realloc
realloc函数的出现让动态内存管理更加灵活
有时候我们发现过去申请的空间太小了,有时候我们⼜会觉得申请的空间过⼤了,那为了合理的内存空间大小,我们⼀定会对内存的空间⼤小做灵活的调整。
而 realloc 函数就可以做到对动态开辟内存⼤小的调整。
void * realloc(void * ptr, size_t size);
ptr 是 要调整的内存地址;
size 是 调整之后的新大小;
返回值为调整之后的内存起始位置。
如果ptr为NULL,那此时的realloc和malloc等价;
如果ptr不为空:
1、size调整到比原空间小,realloc返回值还是ptr,只不过ptr指向的空间变小了,发生了截断(被截断掉的内存空间不需要你来操心释放的问题,因为realloc内部已经帮我们完成了);
2、size调整到比原空间大
(1)原空间后边有足够大的空间用于扩展的话,
realloc返回值还是ptr,只不过ptr指向的空间变大了,而且扩展空间的内容是不确定的;
(2)原空间后边没有足够大的空间用于扩展的话,
在堆空间上另找一块size大小的连续空间,并把原空间中的内容拷贝到新空间中,而扩展空间的内容是不确定的。然后释放原空间,最终返回新空间。
注意:
如果realloc函数未能分配所请求的内存块,则返回空指针,并且不会释放由参数ptr指向的内存块(它仍然有效,并且其内容不变)。

	int *ptr = (int *)malloc(100);
	if(NULL != ptr)
		//业务处理
	else
		exit(EXIT_FAILURE);
	//ptr = realloc(ptr, 1000);//如果realloc失败,返回空指针,而且不会释放由参数ptr指向的内存块,
	                                      //这样一来,ptr所指向的那块空间就被“弄丢”了,造成内存泄漏。
	int *p = NULL;
	p = realloc(ptr, 1000);
	if(NULL != p)
	{
		ptr = p;
		p = NULL;
	}
	//业务处理
	free(ptr);

常见的动态内存错误

1、对NULL指针的解引用;
2、对动态开辟空间的越界访问

	int *p = (int *)malloc(10 * sizeof(int));
	if(NULL == p)
		exit(EXIT_FAILURE);
	for(int i = 0; i <= 10; ++i)
		*(p+i) = i;//当i是10的时候越界访问
	free(p);

3、对非动态开辟内存使用free函数进行释放

	int a = 10;
	int *p = &a;
	free(p);//如果参数 p 指向的空间不是动态开辟的,那free函数的⾏为是未定义的。

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

    int *p = (int *)malloc(100);
	p++;
	free(p);

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

	int *p = (int *)malloc(100);
	free(p);
	free(p);//注意:一块动态内存只能被释放一次。

6、动态开辟内存忘记手动free释放(造成内存泄漏)
切记:
动态开辟的空间⼀定要释放,并且正确释放(4和5就是犯了严重的错误)。
例题1

void GetMemory(char *p)
{
	p = (char *)malloc(100);
}

int main()
{
	char *str = NULL;
	GetMemory(str);
	strcpy(str, "hello world");//出错,因为str的值还是NULL。
	printf("%s\n", str);
}
//没有释放动态申请的内存

例题2

char *GetMemory()
{
	char p[] = "hello world";
	return p;
}

int main()
{
	char *str = NULL;
	str = GetMemory();
	printf("%s\n", str);//不是hello world
}

例题3

void GetMemory2(char **p, int num)
{
	*p = (char *)malloc(num);
}

int main()
{
	char *str = NULL;
	GetMemory2(&str, 100);
	strcpy(str, "hello world");
	printf("%s\n", str);//打印出hello world
}//申请的内存没判断,而且申请的内存没释放。

例题4

int main()
{
	char *str = (char *)malloc(100);
	strcpy(str, "hello");//如果malloc,str为NULL,所以在使用str前需要判空。
	free(str);
	if(NULL  != str)
	{
		strcpy(str, "world");//非法操作,因为str指向的空间已经被释放了。
		                              //只不过free并不会修改str的值。
		printf(str);//输出world
	}
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值