• C进阶—动态内存管理


    目录

    一、动态内存管理的原因

    二、堆区分配内存

    三、动态空间操作(如何申请空间、如何释放)

    (二)void *malloc(size_t size)

    (二)void free(void *start)

    (三)void *calloc(size_t num,size_t size )

    (四)void*realloc(void *memblock ,size_t size)

    四、常见的动态内存错误

    五、动态内存管理注意事项

    六、柔性数组

    (一)柔性数组的定义

    (二)柔性数组的特点

    (三)柔性数组的使用

    (四)柔性数组的优势


    一、动态内存管理的原因

    1.变量名称是一种外在体现,是给程序员看的,程序内部指令都是通过地址进行操作的

    2.每个函数在调用的时候都会分配一块栈空间,作为函数的局部存储,函数内定义的局部变量,使用空间都是在函数栈空间中分配的。

    一个 函数内其实如果定义局部变量并不是无限制定义的,如果定义一个超大变量,超过了函数空间大小就会报错。

    在一个函数中定义一个变量的注意事项:

    局部变量:栈空间上内存分

    1.不能过大,会超出函数栈空间

    2.变量出了作用域空间就会被释放

    3.当我们有一些需求:保存一组数据,但是也不知道数据有多少个,因此只能按照上限个数提前定义好数组,但是会造成空间浪费,很多实际情况用不了。基于以上的局部变量的一些限制,提出堆上内存分配。

    二、堆区分配内存

                                                                                                        0xFFFFFFFF

    栈    分配地址从高往低,先定义的变量地址高。空间大小不固定,程序编译阶         段无法确定。如函数递归,栈空间不断分配。

                                

    堆     分配地址从低往高,先定义的变量地址低。空间大小不固定,程序编译             阶段无法确定。如动态空间申请。
    数据段    所占用空间大小在编译完毕后是固定的
    代码段    所占用空间大小在编译完毕后是固定的
                                                                                                        0x00000000

    在堆区分配内存——动态内存分配——分内存有个特点:

    1.手动申请,手动释放(出了申请位置的作用域也不会释放)

    2.需要多少申请多少(按需分配)

    3.空间在堆上分配,不占用栈空间(因此不怕栈溢出)

    三、动态空间操作(如何申请空间、如何释放)

    (二)void *malloc(size_t size)

    功能:申请size字节大小的空间

    返回值:成功则返回申请到的空间的首地址;失败则返回NULL

    注意:在动态空间申请的时候一定要进行返回值的判断,因为空间申请有可能失败

    (二)void free(void *start)

    功能:释放动态申请的空间参数,参数start是动态申请空间时返回的首地址

    注意:1.不要对局部变量进行free;2.空间释放只能从动态申请的首地址释放,不能是从中途释

    动态地址分配,申请空间是一整块连续空间,因此可以当做数组来用

    (三)void *calloc(size_t num,size_t size )

    功能:为 num 个大小为 size 的元素开辟一块空间,并且把空间的每个字节初始化为0

    返回值:成功则返回申请到的空间的首地址;失败则返回NULL

    (四)void*realloc(void *memblock ,size_t size)

    功能:重新为原来的空间扩容。原先使用的空间必须有足够的空闲空间,否则就申请新的空间,将数据拷贝过去,返回新地址。

    memblock:原先老的空间首地址;          size:要扩容到的大小;

    注意:成功则会释放原先的空间,返回新的空间首地址(在原地址扩容成功则不会释放),失败返回NULL。realloc如果传入的源空间地址为空则相当于malloc。

    扩容的两种情况:

    情况1:原有空间之后有足够大的空间

    要扩展内存就直接原有内存之后直接追加空间,原来空间的数据不发生变化。

    情况2:原有空间之后没有足够大的空间

    原有空间之后没有足够多的空间时,扩展的方法是:在堆空间上另找一个合适大小的连续空间来
    使用。这样函数返回的是一个新的内存地址。

    四、常见的动态内存错误

    1.对NULL指针的解引用操作

    2.对动态开辟空间的越界访问

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

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

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

    6.动态开辟内存忘记释放(内存泄漏)

    ps:

    野指针:指向了一块不能正常访问的空间

    如果申请了一块内存,将地址赋值给了指针变量p,然后对p进行free,这时候只是把指针p指向的空间释放了并没有修改p变量的指向。意味着这首p指针就是一个野指针。

    五、动态内存管理注意事项

    malloc和free一定成对出现。

    1.不要对局部变量的地址进行释放

    2.动态申请的空间不要从中释放

    3.释放了指针变量指向的空间后,指针变量会变为野指针,要注意指向,不要继续访问原员空间,

    4.动态申请的空间不要重复释放

    5.扩容成功后,有可能原空间会被释放掉,因此一定要保存新的地址

    6.申请与释放一定要成对出现

    六、柔性数组

    (一)柔性数组的定义

    简单理解不是一个数组,是和结构体搭配使用的一种数据结构。

    定义:在一个结构体中。定义一个没有大小的数组,数组的空间通过动态内存申请。

    例:

    1. typedef struct st_type
    2. {
    3. int i;
    4. int a[0];//柔性数组成员
    5. }type_a;
    1. typedef struct st_type
    2. {
    3. int i;
    4. int a[];//柔性数组成员
    5. }type_a;

    (二)柔性数组的特点

    1.结构中的柔性数组成员前面必须至少一个其他成员,柔性数组要放在结构体的最后边。
    2.sizeof 返回的这种结构大小不包括柔性数组的内存。
    3.包含柔性数组成员的结构用malloc ()函数进行内存的动态分配,并且分配的内存应该大于结构的大小,以适应柔性数组的预期大小。

    1. typedef struct st_type
    2. {
    3. int i;
    4. int a[0];//柔性数组成员
    5. }type_a;
    6. printf("%d\n", sizeof(type_a));//输出的是4

    (三)柔性数组的使用

    1. int i = 0;
    2. type_a *p = (type_a*)malloc(sizeof(type_a)+100*sizeof(int));
    3. //业务处理
    4. p->i = 100;
    5. for(i=0; i<100; i++)
    6. {
    7. p->a[i] = i;
    8. }
    9. free(p);

    (四)柔性数组的优势

    1.方便内存释放。

    2.这样有利于访问速度。

  • 相关阅读:
    洛谷 P1075 [NOIP2012 普及组] 质因数分解
    Springboot整合RedisTemplate
    Ansible简介
    C++ 01.学习C++的意义-狄泰软件学院
    phpStudy下载(安装)-图文详解(windows)
    机器视觉康耐视visionpro-脚本常见的编辑编译错误和运行错误及警告性错误,调试解决办法
    sklearn机器学习——day04
    Java 复习笔记 - 常见算法:排序算法
    大家都能看得懂的源码 - 如何封装 cookie/localStorage/sessionStorage hook?
    武汉星起航:亚马逊卖家如何做好产品的差异化工作?
  • 原文地址:https://blog.csdn.net/m0_63372226/article/details/126206860