• C语言动态内存空间分配


    1. 前言

            在讲内存分配前,咱来聊一下为什么会有内存分配这个概念呢,大家都知道C语言当中是有着许多的数据类型,使用这些数据类型就会在内存上开辟其相对应的空间,那既然会开辟相应的空间,为什么还会有内存分配呢,我们会发现C语言分配内存空间,是已经被固定好了的,固定开辟多少空间,但是这样开辟空间会不会太死板了呢,如果我用不上这些空间呢,这样会导致内存的利用率很低,有没有一种,我需要多少空间,就开辟多少空间的方法呢,这个时候C语言就为我们引进了动态内存分配的这个概念,顾名思义,就是你想分配多少空间就分配多少空间,下边我们来一起了解一下怎样在内存上开辟我们想要的空间

    (注意:使用这些函数都需要包含#include<stdlib.h>这个头文件)

    2. malloc函数:

    2.1 malloc函数的声明:

    函数向内存申请⼀块连续可⽤的空间,并返回指向这块空间的指针(由于不知道你要申请的类型,使用返回值为void*)

    1. 如果开辟成功,则返回⼀个指向开辟好空间的指针。
    2. 如果开辟失败,则返回⼀个 NULL 指针,因此malloc的返回值⼀定要做检查
    3.  如果参数 size 为0,malloc的⾏为是标准是未定义的取决于编译器决于编译器(vs2022)

    所以一个malloc的使用可以分为以下几步:

    1. 使用malloc函数对内存空间申请指定空间
    2. 判断返回值是否为NULL
    3. 内存空间的释放

    2.2 malloc函数的简单应用:

    1. #include <stdlib.h>
    2. int main()
    3. {
    4. int* p = (int*)malloc(sizeof(int) * 5); //向内存申请5个int型的内存空间
    5. if (p == NULL) //判断返回值是否为空
    6. {
    7. perror("malloc"); //为空,则报错malloc函数处有问题
    8. return 1;
    9. }
    10. free(p); //释放内存,在下边会说喔
    11. p = NULL; //将p指针赋为NULL,防止出现野指针
    12. return 0;
    13. }

    3. free函数:

            在前边的代码当中,我们了解到free函数是专门用来释放动态申请的内存空间,同时也说明了它只能释放动态申请的内存空间,关于这一点兄弟们不要搞混咯

    3.1 free函数声明:

    ​​​​​

    1. 如果参数 ptr 指向的空间不是动态开辟的,那free函数的⾏为是未定义的
    2. 如果参数 ptr 是NULL指针,则函数什么事都不做
    关于free函数的使用前面有提到,有需要的兄弟可以往上翻翻

    4. malloc与free的实际运用:

    1. int main()
    2. {
    3. int* p = (int*)malloc(sizeof(int) * 10);//申请10个int型元素
    4. if (p == NULL)
    5. {
    6. perror("malloc");
    7. return 1;
    8. }
    9. for (int i = 0; i < 10; i++) //对malloc申请的空间进行赋值
    10. {
    11. *(p + i) = i;
    12. }
    13. for (int i = 0; i < 10; i++) //输出
    14. {
    15. printf("%d ", *(p + i));
    16. }
    17. free(p); //释放空间
    18. p = NULL;
    19. return 0;
    20. }
    21. //执行结果
    22. 0 1 2 3 4 5 6 7 8 9

    5. calloc函数:

    5.1 calloc函数的声明:

    calloc

    void* calloc (size_t num, size_t size);
    1. 与malloc相似,也是用来开辟内存空间
    2. 开辟num个size大小的元素
    3. 相较于malloc来说,calloc开辟好空间后,还会将空间内的每个字节初始化为0

    5.2 calloc的简单应用:

    下边我们来一段代码调试进行了解:

    1. int main()
    2. {
    3. int* p1 = (int*)calloc(10, sizeof(int));
    4. if (p1 == NULL)
    5. {
    6. perror("calloc");
    7. return 1;
    8. }
    9. free(p1);
    10. return 0;
    11. }

    题解分析:

    6 realloc函数:

            当你觉得动态内存申请的空间,不够使用的时候,这个时候就可以运用realloc函数对内存空间进行扩容,所以realloc函数是对动态内存空间进行扩容的

    6.1 realloc函数的声明:

    realloc

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

            1. ptr 是要调整的内存地址

            2. size 为调整之后新大小
            3. 返回值为调整之后的内存起始位置
            4.  这个函数调整原内存空间⼤⼩的基础上,还会将原来内存中的数据移动到新的空间

    6.2 relloc函数扩容内存时的两种情况:

            1. 原有的空间在扩容的时候,后边有足够的空间

            2.  原有的空间在扩容的时候,后边没有足够的空间

    6.3 relloc函数的简单应用:

    1. int main()
    2. {
    3. int* p = (int*)malloc(sizeof(int) * 10);
    4. if (p == NULL)
    5. {
    6. perror("malloc");
    7. return 1;
    8. }
    9. for (int i = 0; i < 10; i++)
    10. {
    11. *(p + i) = i;
    12. }
    13. for (int i = 0; i < 10; i++)
    14. {
    15. printf("%d ", *(p + i));
    16. }
    17. printf("\n");
    18. int* p1 = (int*)realloc(p,sizeof(int) * 15); //扩容5个int型
    19. if (p1 == NULL)
    20. {
    21. perror("realloc");
    22. return 2;
    23. }
    24. for (int i = 10; i < 15; i++)
    25. {
    26. *(p1 + i) = i;
    27. }
    28. for (int i = 0; i < 15; i++)
    29. {
    30. printf("%d ", *(p1 + i));
    31. }
    32. free(p1);
    33. p1 = NULL;
    34. return 0;
    35. return 0;
    36. }
    37. //执行结果
    38. 0 1 2 3 4 5 6 7 8 9
    39. 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14

    6.4 柔性数组:

            在c99中规定,一个结构体中最后的数组如果没有指定大小,则这个数组被称为柔性数组

    柔性数组的特点:

    1. 结构中的柔性数组成员前⾯必须⾄少⼀个其他成员
    2. sizeof 返回的这种结构⼤⼩不包括柔性数组的内存
    3. 包含柔性数组成员的结构用malloc ()函数进行内存的动态分配,并且分配的内存应该大于结构的大小,以适应柔性数组的预期大小
    举例:
    1. struct S
    2. {
    3. int a;
    4. char a1;
    5. int a3[];
    6. }q;
    7. int main()
    8. {
    9. printf("%结构体大小为:zd", sizeof(q)); //计算结构体大小,了解柔性数组是否分配了大小
    10. struct S* s = (struct S*)malloc(sizeof(struct S) + sizeof(int) * 5); //为a3数组分配了5个int型的空间
    11. return 0;
    12. }

    题解分析:

    6.4.1 柔性数组的简单应用:

            申请一块动态内存空间后,对齐扩容,扩大柔性数组的内存空间,然后对柔性数组进行赋值,最后输出结构体中成员的值:

    1. //柔性数组
    2. struct S
    3. {
    4. int a;
    5. char a1;
    6. int a3[];
    7. }q;
    8. int main()
    9. {
    10. int ret = sizeof(struct S);
    11. struct S* p2 = (struct S*)malloc(sizeof(struct S));
    12. if (p2 == NULL)
    13. {
    14. perror("malloc");
    15. return 1;
    16. }
    17. p2->a = 100;
    18. p2->a1 = 48;
    19. struct S* p3 = (struct S*)realloc(p2, sizeof(struct S)+sizeof(int)*5); //扩容柔性数组的大小
    20. printf("%zd\n", sizeof(p3));
    21. if (p3 == NULL)
    22. {
    23. perror("realloc");
    24. return 2;
    25. }
    26. p2 = NULL;
    27. for (int i = 0; i < 5; i++)
    28. {
    29. p3->a3[i] = i;
    30. }
    31. printf("%d\n", p3->a);
    32. printf("%d\n", p3->a1);
    33. for (int i = 0; i < 5; i++)
    34. {
    35. printf("%d ", p3->a3[i]);
    36. }
    37. free(p3);
    38. p3 = NULL;
    39. return 0;
    40. }
    41. //执行结果
    42. 8
    43. 100
    44. 48
    45. 0 1 2 3 4

    7. 常见的动态内存错误:

    7.1 使用free对非动态内存进行释放:

    1. int main()
    2. {
    3. int a = 10;
    4. int* p = &a;
    5. free(p); //free对非动态内存申请的空间进行释放
    6. return 0;
    7. }

    执行结果:

    7.2 动态内存越界访问:

    1. int main()
    2. {
    3. int* p = (int*)malloc(sizeof(int) * 5);//向内存动态申请5个int型空间
    4. if (p == NULL)
    5. {
    6. perror("malloc");
    7. return 1;
    8. }
    9. for (int i = 0; i < 6; i++) //向p执行的空间放入6个int型元素
    10. {
    11. *(p + i) = i;
    12. }
    13. return 0;
    14. }

    执行结果:

    (程序死循环加上报错)

    7.3 使⽤free释放⼀块动态开辟内存的⼀部分:

    1. int main()
    2. {
    3. int* p = (int*)malloc(sizeof(int) * 5);
    4. if (p == NULL)
    5. {
    6. perror("malloc");
    7. return 1;
    8. }
    9. for (int i = 0; i < 5; i++)
    10. {
    11. *p = i;
    12. p++; //每执行一次p++,则p指向的地址加1
    13. }
    14. free(p);
    15. return 0;
    16. }

    题解分析:

    执行结果:

    (程序死循环加上报错)

    7.4 对同⼀块动态内存多次释放

    1. int main()
    2. {
    3. int* p = (int*)malloc(sizeof(int) * 5);
    4. if (p == NULL)
    5. {
    6. perror("malloc");
    7. return 1;
    8. }
    9. free(p);
    10. free(p);
    11. return 0;
    12. }

    执行结果:

    (程序死循环加上报错)

    7.5 动态开辟内存忘记释放(内存泄漏):

            什么意思呢,简单来讲,就是你申请了空间,但是没有及时的将空间释放掉,与上边多次释放刚好相反:

    1. int main()
    2. {
    3. int* p = (int*)malloc(sizeof(int) * 5);
    4. if (p == NULL)
    5. {
    6. perror("malloc");
    7. return 1;
    8. }
    9. //free(p); //为注释掉的内容
    10. //free(p);
    11. return 0;
    12. }

    执行以后不会报错,但是会造成内存上的浪费


    7.6 对NULL指针的解引⽤操作

    1. int main()
    2. {
    3. int* p = (int*)malloc(INT_MAX*10);
    4. *p = 100;
    5. free(p);
    6. return 0;
    7. }

    由于并没有判断p是否为NULL,当p为NULL时,就会发生以下报错:

    (死循环直至程序崩溃)

    (今日分享到此结束,如觉得对您有帮助,还请点赞关注支持一下,Thanks♪(・ω・)ノ!!!)

  • 相关阅读:
    java汽车租赁超时罚款系统springboot+vue-前后端分离-e36ht
    布隆过滤器简单实现添加和判断功能
    SQL分类
    阿里云高庆瑞:高弹性、高可用、低成本的云上资源管理最佳实践
    Rpc-实现Client对ZooKeeper的服务监听
    小技巧(12):关于PC端简单的视频剪辑处理中,bandicam(录制)、pr(配音)、pr(导出)、剪映(字幕识别)、pr(最终版导出)的全过程及基础设置
    【VS】以VS为IDE的报错#?may be unsafe
    C语言-整数和浮点数在内存中的存储-详解-上
    SpringBoot项目创建方式一:Spring Initializr(Web界面方式)
    成功解决 IDEA 2020 版本 代码报错不提示的几种方案
  • 原文地址:https://blog.csdn.net/czzlv/article/details/137284898