深入理解动态内存管理

动态内存分配在C语言中用于解决栈空间限制和运行时确定内存需求的问题。malloc用于动态开辟内存,其返回的指针需要检查是否为空。free负责释放内存,避免内存泄漏。calloc与malloc类似,但会初始化内存为0。realloc则用于调整已分配内存的大小,需要注意当空间扩展失败时,原始数据可能丢失。在使用realloc时,为确保数据安全,通常需要先分配新内存,再释放旧内存。
摘要由CSDN通过智能技术生成

1、为什么存在动态内存分配?
我们之前为数据开辟的空间都是在栈上开辟的,但是,在栈上开辟空间有几个缺点
第一个缺点:开辟空间小,没办法使用大空间,当开辟大空间之后,系统会发生错误。
第二个缺点:开辟空间是固定的,然而有的时候我们需要的空间在程序运行的时候才能知道。
基于以上两个缺点,就出现了动态内存开辟的解决方法。
2、动态内存函数的介绍
(1)malloc和free
a、malloc
动态内存开辟函数:

void * malloc(size_t size);

这个函数向内存申请一块连续可用的空间,并返回指向这块空间的指针。
●如果开辟成功,则返回一个指向开辟好空间的指针。
●如果开辟失败,则返回一个NULL指针,因此malloc的返回值一定要做检查。
●返回值的类型是void*,所以malloc函数并不知道开辟空间的类型,具体在使用的时候使用者自己来确定。
●如果size为0,malloc的行为是标准是未定义的,取决于编译器。
b、free
free函数是专门用来做动态内存的释放和回收的,函数原型如下:

void free(void * ptr);

free函数用来释放动态开辟的内存。
●如果参数ptr指向的空间不是动态开辟的,那free函数的行为就是未定义的。
●如果参数ptr是NULL指针,则函数什么事都不做。
●free的时候,是将开辟空间内的数据变为随机值,并不是变为0。
注意:(1)malloc申请的空间在堆上申请。
(2)因为malloc是函数,所以堆空间只能在运行时确定。
(3)malloc在堆上申请空间,程序员申请,程序员释放。如果不释放,会有内存泄漏问题。程序退出,内存 泄漏问题随之消失。
(4)free时释放的是指针和内存块(堆)的关系。
(5)malloc的时候,并不是申请多少字节就开辟多少字节,开辟的字节是比程序员申请的字节要多的。多余的字节里面放的是元信息,比如开辟的时间、什么人开辟的等等一系列的信息。所以用malloc开辟空间的时候,开辟的越多越合适,毕竟无论你要开辟多少字节,都必须开辟一段字节用来保存元信息。free的时候释放的字节是全部空间,包括元信息所占的空间。

我们来举一个例子了解一下malloc和free的使用:

#include<stdio.h>
#include<stdlib.h>
int main(){
int num=0;
printf("请输入要开辟的空间大小:")
scanf("%d",&num);
char * ptr=NULL;
ptr=(char * )malloc(num*sizeof(char));
if(NULL!=ptr)//判断ptr指针是否为空
{
int i=0;
for(i=0;i<num;i++)
    {
       *(ptr+i)=0;
    }
}
free(ptr);//释放ptr所指向的动态内存
ptr=NULL;//此时,ptr指向的是随机值,ptr就变成了野指针。这一步很有必要。
return 0;
}

2、calloc
calloc也是用来进行动态内存分配的,与malloc的区别是calloc会初始化。
原型如下:

void * calloc(size_t num,size_t size);

●函数的功能是为num个大小为size的元素开辟一块空间,并把空间的每个字节初始化为0。
●calloc也需要free释放。
3、realloc
在进行malloc动态内存开辟的时候,可能会发现开辟的空间多了或者是少了,那这个时候就需要用到realloc函数了。realloc函数就可以做到对动态开辟内存大小的调整。
函数原型如下:

void * realloc(void * ptr,size_t size);

●ptr是要调整的内存地址
●size调整之后新大小
●返回值为调整之后的内存起始位置
●这个函数调整原内存空间大小的基础上,还会将原来内存中的数据移动到新的空间。

调整内存大小时分为两种,一种是调大,一种是调小。调小直接在开辟的内存上free即可,调大不行。
调大有两种情况:
情况1:原有空间之后有足够大的空间,直接向后扩容即可。
在这里插入图片描述

情况2:原有空间之后没有足够大的空间,则需要重新开辟空间。
在这里插入图片描述在情况2的时候,就要注意一些问题,举个例子:

#include <stdio.h>
#include<stdlib.h>
int main()
{
    int *ptr = malloc(100);     
    if(ptr != NULL)
    {
        //业务处理
    }
    else
    {
       exit(EXIT_FAILURE);
    }
    //扩展容量
    //代码1
    ptr = realloc(ptr, 1000);//这样可以吗?(如果申请失败会如何?)
    //代码2
    int*p = NULL;
    p = realloc(ptr, 1000);     
    if(p != NULL)
    {
       ptr = p;
    }
    //业务处理
    free(ptr);
    return 0;
}

明显,代码1就不严谨,如果是情况2的话,开辟失败,依旧将开辟失败的地址赋给ptr,那么就没办法指向原来的数据了,得不偿失。代码2就是正确的,既保证了开辟失败之后,原始数据没有受影响,并且对开辟成功与否做了判断。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值