• 【C/C++】动态申请内存、内存泄漏


    静态分配
    1、 在程序编译或运行过程中,按事先规定大小分配内存空间的分配方式。int a [10]
    2、 必须事先知道所需空间的大小。
    3、 分配在栈区或全局变量区,一般以数组的形式。
    4、 按计划分配。
    动态分配
    1、在程序运行过程中,根据需要大小自由分配所需空间。
    2、按需分配。
    3、分配在堆区,一般使用特定的函数进行分配。

    1、动态申请内存的方式

    虚拟内存的用户区 中的堆区获取一块指定大小的连续内存,并返回其指针。

    (可以类比Java中的堆 、实例对象、对象名,内存泄漏的知识,高级语言设计都是遵循操作系统的一些基础原理的,别觉得有啥不一样)。

    1、malloc

    1. #include
    2. void *malloc(unsigned int size);
    3. 功能:在堆区开辟指定长度的空间,并且空间是连续的
    4. 参数:
    5. size:要开辟的空间的大小
    6. 返回值:
    7. 成功:开辟好的空间的首地址
    8. 失败:NULL
    注意
    1、在调用malloc之后,一定要判断一下,是否申请内存成功。
    2、如果多次malloc申请的内存,第1次和第2次申请的内存不一定是连续的
    3、使用malloc开辟空间需要保存开辟好的空间的首地址,但是由于不确定空间用于做
    什么,所以本身返回值类型为void *,所以在调用函数时根据接收者的类型对其进行强制类型转换。
    1. 1 #include
    2. 2 #include
    3. 3
    4. 4 char *fun()
    5. 5 {
    6. 6 //char ch[100] = "hello world";
    7. 7
    8. 8 静态全局区的空间只要开辟好,除非程序结束,否则不会释放,所以
    9. 9 如果是临时使用,不建议使用静态全局区的空间
    10. 10 static char ch[100] = "hello world";
    11. 11
    12. 12 堆区开辟空间,手动申请手动释放,更加灵活
    13. 13 使用malloc函数的时候一般要进行强转
    14. 14 char *str = (char *)malloc(100 * sizeof(char));
    15. 15 str[0] = 'h';
    16. 16 str[1] = 'e';
    17. 17 str[2] = 'l';
    18. 18 str[3] = 'l';
    19. 19 str[4] = 'o';
    20. 20 str[5] = '\0';
    21. 21
    22. 22 return str;
    23. 23 }
    24. 24
    25. 25 int main(int argc, char *argv[])
    26. 26 {
    27. 27 char *p;
    28. 28 p = fun();
    29. 29 printf("p = %s\n", p);
    30. 30
    31. 31 return 0;
    32. 32 }

    free

    1. #include
    2. void free(void *ptr)
    3. 功能:释放堆区的空间
    4. 参数:
    5. ptr:开辟后使用完毕的堆区的空间的首地址
    6. 返回值:
    注意:
    free函数只能释放堆区的空间,其他区域的空间无法使用free
    free释放空间必须释放malloc或者calloc或者realloc的返回值对应的空间,不能说只释
    放一部分
    free(p); 注意当free后,因为没有给p赋值,所以p还是指向原先动态申请的内存。但是
    内存已经不能再用了,p变成野指针了,所以一般为了放置野指针,会free完毕之后对p赋
    为NULL。
    一块动态申请的内存只能free一次,不能多次free
    正确使用:
    1. //动态申请内存
    2. int *p =(int *)malloc(sizeof(int)*100);
    3. //释放p指向的空间 (但是p仍然指向该位置)
    4. free(p);
    5. //预防野指针(p重新指向0地址)
    6. p=NULL;

    2、calloc

    1. #include
    2. void * calloc(size_t nmemb,size_t size);
    3. 功能:在堆区申请指定大小的空间
    4. 参数:
    5. nmemb:要申请的空间的块数
    6. size:每块的字节数
    7. 返回值:
    8. 成功:申请空间的首地址
    9. 失败:NULL
    注意:
    malloc和calloc函数都是用来申请内存的。
    区别:
    1) 函数的名字不一样
    2) 参数的个数不一样
    3) malloc申请的内存,内存中存放的内容是随机的,不确定的, 而calloc函数申请的内存中的内容为0。
    例如:
     
    char *p=(char *)calloc(3,100);
    
    在堆中申请了3块,每块大小为100个字节,即300个字节连续的区域。

    3、relloc

    1. #include
    2. void* realloc(void *s,unsigned int newsize);
    3. 功能:在原本申请好的堆区空间的基础上重新申请内存,新的空间大小为函数的第二个参数 如果原本申请好的空间的后面不足以增加指定的大小,系统会重新找一个足够大的位
    4. 置开辟指定的空间,然后将原本空间中的数据拷贝过来,然后释放原本的空间
    5. 如果newsize比原先的内存小,则会释放原先内存的后面的存储空间, 只留前面的newsize个字节
    6. 参数:
    7. s:原本开辟好的空间的首地址
    8. newsize:重新开辟的空间的大小
    9. 返回值:
    10. 新的空间的首地址

    注意:malloc calloc relloc 动态申请的内存,只有在free或程序结束的时候才释放。

    2、内存泄露

    内存泄露的概念:
    申请的内存,首地址丢了,找不了,再也没法使用了,也没法释放了,这块内存就被泄
    露了。

    内存泄漏案例1:
    1. int main()
    2. {
    3. char *p;
    4. p=(char *)malloc(100);
    5. //接下来,可以用p指向的内存了
    6. p="hello world";//p指向别的地方了,保存字符串常量的首地址
    7. //从此以后,再也找不到你申请的100个字节了。则动态申请的100个字节就被泄露了
    8. return 0;
    9. }

    内存泄漏案例2:
    1. void fun()
    2. {
    3. char *p;
    4. p=(char *)malloc(100);
    5. //接下来,可以用p指向的内存了
    6. ...
    7. }
    8. int main()
    9. {
    10. //每调用一次fun泄露100个字节
    11. fun();
    12. fun();
    13. return 0;
    14. }

    解决方式1:

    1. void fun()
    2. {
    3. char *p;
    4. p=(char *)malloc(100);
    5. //接下来,可以用p指向的内存了
    6. ...
    7. free(p);
    8. }
    9. int main()
    10. {
    11. fun();
    12. fun();
    13. return 0;
    14. }

    解决方式2:

    1. char * fun()
    2. {
    3. char *p;
    4. p=(char *)malloc(100);
    5. //接下来,可以用p指向的内存了
    6. ...
    7. return p;
    8. }
    9. int main()
    10. {
    11. char *q;
    12. q=fun();
    13. //可以通过q使用 ,动态申请的100个字节的内存了
    14. //记得释放
    15. free(q);
    16. //防止野指针
    17. q = NULL;
    18. return 0;
    19. }
    总结:申请的内存,一定不要把首地址给丢了,在不用的时候一定要释放内存。
  • 相关阅读:
    华为智能高校出口安全解决方案(2)
    香港Web3.0生态现状
    Borland编辑器DOS系统快捷键应用
    水库大坝安全监测预警系统的重要作用
    SQL零基础入门教程,贼拉详细!贼拉简单! 速通数据库期末考!(七)
    使用devcpp遇到的常见错误解决方法
    双软认证需要什么条件
    [MapStruct]集合的映射
    Spring(二)
    强化学习调度环境:析取图和离散事件仿真
  • 原文地址:https://blog.csdn.net/Alex_cf/article/details/133700639