• 动态内存管理


    前言:哈喽小伙伴们,虽然已经放假啦,但依然阻止不了我们学习的脚步。

    超越别人的唯一的方法,就是比别人更加努力

    今天我们一起来学习——动态内存管理,掌握如何自己操纵数据空间的大小


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

    我们已经学习过两种内存的开辟方式:

    一是创建单个变量:int a = 10;

    二是创建数组:int arr[10] = {1,2,3,4,5,6,7,8,9,10};

    但是这些方式有个特点,那就是一旦创建之后,我们的内存大小就无法改变

    这样很容易出现内存不足或者内存开辟过多而浪费的情况

    所以为了应对这种问题,C语言让我们程序员能够进行——动态内存管理


    二.动态内存函数

    那我们管理动态内存的方式,便是通过使用四个函数:

    malloc

    free

    calloc

    realloc

    下面我们就来逐一讲解它们的结构和用法。


    1.malloc

     

    使用动态内存函数,都需要头文件:#include

    可以看到,这个函数的返回类型为指针类型,参数为无符号整型。

    size代表的是字节数,也就是说,malloc函数是用来申请一定字节数内存空间的函数。 

     下面我们来尝试使用一下malloc:

    1. #include
    2. #include
    3. int main()
    4. {
    5. int* p = (int*)malloc(10 * sizeof(int));
    6. return 0;
    7. }

    比如说,我们现在要开辟10个整型大小的内存空间,也就是40个字节。

    注意我们的参数要写成上述的这种形式,可以增加代码的可读性,尽量不要直接写成40

    那么既然是开辟整型空间,就要用整型指针来及时接收,同时因为malloc的返回类型为void*,还要进行强制类型转换

    p指针会指向malloc函数所开辟的空间的起点,直到10个int类型空间为止。

    这个时候,p就可以作为一个数组去使用了,为开辟的空间填上数据。

    1. #include
    2. #include
    3. int main()
    4. {
    5. int* p = (int*)malloc(10 * sizeof(int));
    6. int i = 0;
    7. for (i = 0; i < 10; i++)
    8. {
    9. *(p + i) = i;
    10. }
    11. for (i = 0; i < 10; i++)
    12. {
    13. printf("%d ", p[i]);
    14. }
    15. return 0;
    16. }

     

    但是有一个问题,我们的内存空间是有限的,而且malloc所选择开辟的空间是随机的它并不会挑一块很大的没有被占用的空间给你开辟

    这就说明,有可能我们所开辟的空间已经被占用了,这样就会导致开辟失败

    malloc申请开辟内存成功,就会返回空间的起始地址,如果申请失败,则会返回空指针NULL。

    我们是不能够对一个空指针进行使用的,所以我们创建动态内存空间的同时,还要进行判断如果开辟失败,则直接结束程序运行

    1. #include
    2. #include
    3. int main()
    4. {
    5. int* p = (int*)malloc(10 * sizeof(int));
    6. if (p == NULL)
    7. {
    8. return 1;
    9. }
    10. int i = 0;
    11. for (i = 0; i < 10; i++)
    12. {
    13. *(p + i) = i;
    14. }
    15. for (i = 0; i < 10; i++)
    16. {
    17. printf("%d ", p[i]);
    18. }
    19. return 0;
    20. }

     main函数的正常返回值为0,这里我们用1来表示异常返回。

    我们既然是自己申请的空间,用过之后则需要及时将此空间进行释放,那么又该怎么释放呢?

    • 程序退出之后,malloc申请的空间,会被操作系统回收
    • 通过free函数主动释放

    那么既然程序退出就会自动释放,为什么还需要free函数呢???

    实际上,如果让系统自动释放,存在很大的不确定性

    比如程序实际上并没有退出,或者内存空间出现问题而无法释放等等,这样就会造成严重后果

    靠别人不如靠自己,所以我们申请空间之后一定要通过free函数来主动释放


    2.free

     能够看出free函数的参数是一个指针类型,也就是释放这个指针所指向的全部空间

    1. #include
    2. #include
    3. int main()
    4. {
    5. int* p = (int*)malloc(10 * sizeof(int));
    6. if (p == NULL)
    7. {
    8. return 1;
    9. }
    10. int i = 0;
    11. for (i = 0; i < 10; i++)
    12. {
    13. *(p + i) = i;
    14. }
    15. for (i = 0; i < 10; i++)
    16. {
    17. printf("%d ", p[i]);
    18. }
    19. free(p);
    20. }

    那我们就借用上边的代码来使用一下free函数,来看看它具体是如何释放:

    能够看出,free将p指针指向的空间确实释放了但是它并不会改变p指针所指向的位置

    这就类似与一个人突然失去了记忆,但是他还是这个人,只是什么都没有了。

    这样就会导致p成为野指针,我们知道野指针是非常危险的,不能让它存在。所以我们在释放空间之后,紧接着要将p指针置空

         free(p);
        p = NULL;

     同时这里我们还要提醒两点:

    • free只能释放动态开辟的空间,否则不会有任何作用
    • free不能释放空指针,否则也不会有任何作用

    3.calloc

    calloc函数也是用来申请动态内存空间的,但是与malloc有些许不同。

    calloc函数有两个参数,实际上,这两个参数可以认为是malloc函数的参数的分解

    size表示要申请的空间大小

    num表示要申请的空间个数

    比如说我们还是申请10个整型数据空间

    int* p = (int*)calloc(10,sizeof(int));

    这样来看calloc和malloc实际上是一样的,但是它们还有另一个不同:

    1. #include
    2. #include
    3. int main()
    4. {
    5. int* p = (int*)malloc(10 * sizeof(int));
    6. if (p == NULL)
    7. {
    8. return 1;
    9. }
    10. int i = 0;
    11. for (i = 0; i < 10; i++)
    12. {
    13. printf("%d\n", *(p + i));
    14. }
    15. free(p);
    16. p = NULL;
    17. return 0;
    18. }

    来看这个代码,我们用malloc函数申请完空间之后,不做任何处理,来看看打印出来的数据:

    是随机值,但是当我们把malloc换成calloc时:

    1. #include
    2. #include
    3. int main()
    4. {
    5. int* p = (int*)calloc(10 , sizeof(int));
    6. if (p == NULL)
    7. {
    8. return 1;
    9. }
    10. int i = 0;
    11. for (i = 0; i < 10; i++)
    12. {
    13. printf("%d\n", *(p + i));
    14. }
    15. free(p);
    16. p = NULL;
    17. return 0;
    18. }

    得到结果:

    全为0,这就说明calloc在开辟空间时,会进行初始化,而malloc并不会。 


    4.realloc

    realloc函数的作用,是帮助调整由malloc和calloc开辟的空间大小

    realloc函数有两个参数:

    ptr为指针,也就是用于接收malloc或calloc开辟空间的那个指针

    size则是要调整的新的空间大小

    具体使用如下:

    1. #include
    2. #include
    3. int main()
    4. {
    5. int* p = (int*)calloc(10 , sizeof(int));
    6. if (p == NULL)
    7. {
    8. return 1;
    9. }
    10. p = realloc(p, 20 * sizeof(int));
    11. free(p);
    12. p = NULL;
    13. return 0;
    14. }

    但实际上,我们上边的代码存在问题,那就是realloc的返回值不能直接用p接收,因为它也会出现内存开辟失败的问题

    如果开辟失败,同样会返回一个空指针这样不仅没有调整成功,反而原先的空间也不复存在了

    所以我们新建一个指针来接收,并判断是否为空指针不为空则将新指针赋值给p指针

    1. #include
    2. #include
    3. int main()
    4. {
    5. int* p = (int*)calloc(10 , sizeof(int));
    6. if (p == NULL)
    7. {
    8. return 1;
    9. }
    10. int* p1 = realloc(p, 20 * sizeof(int));
    11. if (p1 != NULL)
    12. {
    13. p = p1;
    14. }
    15. free(p);
    16. p = NULL;
    17. return 0;
    18. }

    此外,realloc函数还有一个功能,那就是它其实也可以作为malloc函数使用

    当realloc函数的第一个参数为空指针NULL时,其功能等于malloc函数

    realloc(NULL,sizeof(int)) == malloc(sizeof(int))

    当然这样使用之后还是要进行空指针的判断和使用后的释放操作。 


     三.总结

    有关动态内存管理的知识到这里就结束啦,希望能够对大家有所帮助。

    喜欢博主文章的小伙伴不要忘记一键三连呀!!!

    祝大家国庆节快乐!!我们下期再见!!

  • 相关阅读:
    Androguard Documentation:官方文档阅读笔记
    用过才知道AI配音软件有多方便,推荐四款高度好评的配音工具~
    web前端期末大作业——HTML+CSS简单的旅游网页设计与实现
    小谈设计模式(19)—备忘录模式
    Azure Functions Service Bus Trigger 对容器的支持
    C++ 学习(七)指针、const修饰指针、指针与数组、指针与函数
    less与sass(scss)的区别
    任正非说:我们要改善和媒体的关系,而不是要利用媒体,不要自以为聪明。
    PPP协议(PPP协议链路建立过程、PAP、CHAP、华为虚拟网络设备配置)
    pix2tex - LaTeX OCR 安装使用记录
  • 原文地址:https://blog.csdn.net/2303_78442132/article/details/133437431