动态内存管理,触及本质的最详解析


前言

为什么存在动态内存?首先我们在创建开辟一块空间时,可能存在开辟空间过大 开辟空间不足等现象。这时我们就需要对所开辟空间的大小继续维护管理。


提示:以下是本篇文章正文内容,下面案例可供参考

一、动态内存是什么?

首先计算机的储存空间分为以下几块:栈区、堆区、静态区

大家熟悉的全局变量 int a  等所开辟的空间是在静态区,和局部变量 所开辟的空间在栈区,这了两个区所开辟的空间都不能进行大小的调整。 我们使用动态内存开辟函数开辟的空间,就是在堆区上开辟,可对该区所开辟的内存大小进行调整。

二、如何开辟出动态内存

1.动态内存函数的介绍

malloc    

       ​​                ​​​​​​​        ​​​​​​​        ​​​​​​​        ​​​​​​​        ​​​​​​​        ​​​​​​​        ​​​​​​​        ​​​​​​​     

void * malloc(所开辟空间的字节大小) ;
//eg: int *malloc(5*4);

这个函数向内存申请一块连续的空间,并返回指向该空间地址的指针

 若开辟成功则返回一个开辟好空间的指针

若开辟失败则返回一个空指针 NULL,因此malloc返回的值一定要先判断是否为NULL

若所开辟空间的大小为0;malloc行为是标准未定义的,取决于编译器   

free

free函数用来释放malloc、calloc、realloc所开辟函数的空间

void free(void*p);

若 指针p所指向的空间不是动态内存开辟的,则free函数的行为是未定义的;

若指针p为NULL,则free函数 什么都不做;

注意!!!  free 完一块空间时,表示将该空间返还回去不再使用,并不是直接将p改为NULL,所以一般free掉一块空间时,手动再令 p=NULL。

废话不多说 直接上栗子!

int main()
{
    int*ptr=malloc(5*4);
    if(ptr != NULL)
    {
        int i=0;
        for(i=0;i<5;i++)
        {
            *(ptr+i)=i;
        }
    }
    free(ptr);
    ptr=NULL;    
    return 0;
}

calloc

void* calloc(size_t num,size_t size);
//开辟一个 int型个数为10的内存
int*calloc(10,4);

函数calloc 是为num个大小为size的字节开辟空间,并且把空间的每个字节初始化为0;

calloc 与malloc的区别是 在返回地址之前 把空间的每个字节初始化为0;

realloc

void*realloc(void * ptr,size_t size);
// ptr  指要调整的内存地址;size__t size指要重新调整内存的大小

realloc函数的出现让动态内存管理更加灵活;

返回值为重新调整内存的起始位置,该函数在调整原内存大小的基础上还会把原来内存中的数据移动到新调整内存中去。

realloc在调整内存时存在两种情况:

情况一: 原空间之后有足够大的空间可以在之后继续增加内存空间

情况二:原空间之后没有足够大的空间,需要从堆区另一地方重新开辟一块足够大的空间

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

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

废话不多说 上栗子!

#include<stdio.h>

int main()
{
    int*p=(int*)malloc(5*4);
    if(p!=NULL)
    {
        //业务处理
    }
    else
    {
        return 0;
    )
    int*ptr=(int*)realloc(p,10*4);
    if(ptr!=NULL)
    {
        p=ptr;    
    
    }
    free(p);
    p=NULL;
    return 0;
}

2.常见的动态内存错误

2.1 对NULL的解引用操作

void *test()
{
    int *p=(int*)malloc(INT_MAX);  //开辟空间失败  返回p=NULL
    *p=20; //非法访问空间
    free(p);
}

2.2 对动态内存空间的越界访问

void*test()
{
    int*p=(int *)malloc(5*4);
    if(p==NULL)
    {
        exit(EXIT_FALURE);
    }
    int i=0;
    for(i=0;i<10;i++)
    {
        *(p+i)=i;    //只开辟了5个int的字节,但是使用内存时,i>4时越界访问了。
    }
    free(p);
    p=NULL;
    
    return 0;
}

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

int a=10;
int *p=&a;
free(p);     //int a 是全局变量,所用空间在静态区开辟,free只能释放堆区的动态内存
p=NULL;

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

void *test()
{
    int*p=(int*)malloc(5*4);
    p++;
    free();    //此时p并非指向开辟空间的起始位置,程序崩溃
}

2.5 对同一块空间的多次释放

2.6动态内存开辟的空间忘记释放

#include<stdio.h>
void* test()
{
    int*p=(int*)malloc(40);
    *p=20;
}
int main()
{

    while(1);
    test();

    return 0;
}
//存在内存泄漏。一直开辟空间,没有释放空间,最终导致系统空间消耗殆尽。

该处使用的url网络请求的数据。


总结. C/C++程序的内存开辟

C/C++ 程序内存分配的几个区域:
1. 栈区( stack ):在执行函数时,函数内局部变量的存储单元都可以在栈上创建,函数执行结
束时这些存储单元自动被释放。栈内存分配运算内置于处理器的指令集中,效率很高,但是
分配的内存容量有限。 栈区主要存放运行函数而分配的局部变量、函数参数、返回数据、返
回地址等。
2. 堆区( heap ):一般由程序员分配释放, 若程序员不释放,程序结束时可能由 OS 回收 。分
配方式类似于链表。
3. 数据段(静态区)( static )存放全局变量、静态数据。程序结束后由系统释放。
4. 代码段:存放函数体(类成员函数和全局函数)的二进制代码。
  • 4
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值