• C语言C位出道心法(五):内存管理




    C语言C位出道心法(一):基础语法

    C语言C位出道心法(二):结构体|结构体指针|链表

    C语言C位出道心法(三):共用体|枚举

    C语言C位出道心法(四):文件操作

    C语言C位出道心法(五):内存管理




     一:C语言内存管理认知

    内存管理认知升维:
        在运行程序时,系统将需要的数据都组织存放在内存空间,以备使用。在使用C语言开发软件过程中,常常需要动态地分配和销毁内存空间。
     例如,对动态链表中的节点进行插入和删除,就需要对内存进行管理;
     (2)内存组织方式,程序在开发完成后,需要先装载到计算机的内核或者半导体内存中,再运行程序;
        通常的内存模型体系如下:
       2.1 可执行代码区
    2.2 常量
    2.3 未初始化的变量
    2.4 已经初始化的变量
    2.5 堆区
    2.6 栈区
     (3)内存模型可以总结为4个逻辑阶段
        3.1 可执行代码
     3.2 静态数据。给可执行代码和静态数据分配固定的存储位置。
     3.3 堆(动态数据),程序请求动态分配的内存来自内存池
     3.4 栈。局部数据对象,函数的参数以及调用函数和被调用函数的联系放在称为"栈"的内存池中;
     根据操作平台和编译器的不同,堆和栈既可以是被所有同时运行的程序共享的操作系统资源,也可以是使用程序独占的局部资源;
     (4)堆与栈
        通过内存组织方式可以看到,堆用来存放动态分配内存空间,而栈用来存放局部数据对象,函数的参数以及调用函数和被调用函数的联系;
     4.1 堆,在内存的全局存储空间中,用于程序动态分配和释放的内存块称为"自由存储空间",通常也称之为"堆";
         在C程序中,使用malloc()和free()函数来从堆中动态分配和释放内存。
     4.2 栈,程序不会像处理堆那样的栈中显示地分配内存。当程序调用函数和声明局部变量时,系统间自动分配内存;
         栈是一个"后进先出"的压入弹出式的数据结构体,后进先出是栈明显区别于堆的标志。


    二:C语言中内存堆|栈认知


    2.1 实例一:内存动态分配与释放

    1. #include
    2. #include
    3. /*
    4. 内存管理认知升维:
    5. 在运行程序时,系统将需要的数据都组织存放在内存空间,以备使用。在使用C语言开发软件过程中,常常需要动态地分配和销毁内存空间。
    6. 例如,对动态链表中的节点进行插入和删除,就需要对内存进行管理;
    7. (2)内存组织方式,程序在开发完成后,需要先装载到计算机的内核或者半导体内存中,再运行程序;
    8. 通常的内存模型体系如下:
    9. 2.1 可执行代码区
    10. 2.2 常量
    11. 2.3 未初始化的变量
    12. 2.4 已经初始化的变量
    13. 2.5 堆区
    14. 2.6 栈区
    15. (3)内存模型可以总结为4个逻辑阶段
    16. 3.1 可执行代码
    17. 3.2 静态数据。给可执行代码和静态数据分配固定的存储位置。
    18. 3.3 堆(动态数据),程序请求动态分配的内存来自内存池
    19. 3.4 栈。局部数据对象,函数的参数以及调用函数和被调用函数的联系放在称为"栈"的内存池中;
    20. 根据操作平台和编译器的不同,堆和栈既可以是被所有同时运行的程序共享的操作系统资源,也可以是使用程序独占的局部资源;
    21. (4)堆与栈
    22. 通过内存组织方式可以看到,堆用来存放动态分配内存空间,而栈用来存放局部数据对象,函数的参数以及调用函数和被调用函数的联系;
    23. 4.1 堆,在内存的全局存储空间中,用于程序动态分配和释放的内存块称为"自由存储空间",通常也称之为"堆";
    24. 在C程序中,使用malloc()和free()函数来从堆中动态分配和释放内存。
    25. 4.2 栈,程序不会像处理堆那样的栈中显示地分配内存。当程序调用函数和声明局部变量时,系统间自动分配内存;
    26. 栈是一个"后进先出"的压入弹出式的数据结构体,后进先出是栈明显区别于堆的标志。
    27. */
    28. int main() {
    29. char* pInt; // 定义字符型的指针变量
    30. pInt = (char*)malloc(sizeof(char)); // 动态给pInt变量分配内存;
    31. // 使用分配的内存
    32. *pInt = 65; // 给变量赋值
    33. printf("the graph is : %c\n", *pInt); // 输出显示图形
    34. free(pInt); // 释放pInt变量所占用的内存
    35. return 0;
    36. }

    运行效果:


    the graph is : A

    D:\program_file\C++_workspace\ProjectToMemoryDynamicManager\x64\Debug\ProjectToMemoryDynamicManager.exe (进程 9316)已退 出,代码为 0。
    要在调试停止时自动关闭控制台,请启用“工具”->“选项”->“调试”->“调试停止时自动关闭控制台”。
    按任意键关闭此窗口. . .
     



     2.2 实例二: 给变量动态分配内存空间

      内存的动态管理
      (1)malloc()函数原型如下:
            void *malloc(unsigned int size);
            在stdlib.h头文件中包含函数,作用是在内存中动态分配一块size大小的内存空间。malloc()函数会返回一个指针;
            该指针指向分配的内存空间,如果出现错误,则返回NULL;
            (2)使用malloc()函数分配的内存空间在堆中,而不是在栈中,因此在使用万这块内存空间之后一定要将其释放掉,释放内存空间使用的是free()函数;
            (3)C语言规定,如果所申请的内存空间分配不成功,malloc()函数的返回值为null pointer,也即是NULL。在这种情况下,如果进行执行之后的代码,程序就会产生崩溃。
            所以在申请分配内存空间后,都应该及时检测内存空间分配是否成功;


    1. #include
    2. #include
    3. /*
    4. 内存的动态管理
    5. (1)malloc()函数原型如下:
    6. void *malloc(unsigned int size);
    7. 在stdlib.h头文件中包含函数,作用是在内存中动态分配一块size大小的内存空间。malloc()函数会返回一个指针;
    8. 该指针指向分配的内存空间,如果出现错误,则返回NULL;
    9. (2)使用malloc()函数分配的内存空间在堆中,而不是在栈中,因此在使用万这块内存空间之后一定要将其释放掉,释放内存空间使用的是free()函数;
    10. (3)C语言规定,如果所申请的内存空间分配不成功,malloc()函数的返回值为null pointer,也即是NULL。在这种情况下,如果进行执行之后的代码,程序就会产生崩溃。
    11. 所以在申请分配内存空间后,都应该及时检测内存空间分配是否成功;
    12. */
    13. int main() {
    14. int* iIntMalloc = (int*)malloc(sizeof(int)); // 给指针变量分配内存空间;
    15. // 使用内存空间
    16. if (iIntMalloc != NULL) {
    17. *iIntMalloc = 102400; // 使用该内存空间保存数据
    18. printf("衣服有%d件\n", *iIntMalloc); // 输出数据
    19. }
    20. else {
    21. printf("为iIntMalloc指针申请内存空间异常");
    22. }
    23. return 0;
    24. }

    运行效果: 

    衣服有102400件

    D:\program_file\C++_workspace\ProjectToMemoryDynamicManagerFree\x64\Debug\ProjectToMemoryDynamicManagerFree.exe (进程 10252)已退出,代码为 0。
    要在调试停止时自动关闭控制台,请启用“工具”->“选项”->“调试”->“调试停止时自动关闭控制台”。
    按任意键关闭此窗口. . .
     



    2.3 calloc()函数分配内存

    calloc()函数的原型如下:
          void *calloc(unsigned n,unsigned size);
       使用该函数也要包括头文件stdlib.h,其功能是在内存中动态分配n个长度为size的连续内存空间组。
       call()函数会返回一个指针,该指针指向动态分配的连续的内存空间地址。当分配内存空间发送错误时,返回NULL.


    1. #define _CRT_SECURE_NO_WARNINGS
    2. #include
    3. #include
    4. #include
    5. /*
    6. calloc()函数的原型如下:
    7. void *calloc(unsigned n,unsigned size);
    8. 使用该函数也要包括头文件stdlib.h,其功能是在内存中动态分配n个长度为size的连续内存空间组。
    9. call()函数会返回一个指针,该指针指向动态分配的连续的内存空间地址。当分配内存空间发送错误时,返回NULL.
    10. */
    11. int main() {
    12. char* ch; // 定义指针
    13. ch = (char*)calloc(30, sizeof(char)); // 分配内存空间
    14. if (ch != NULL) {
    15. strcpy(ch, "中国魔都北京总舵"); // 复制字符串
    16. printf("%s\n", ch);
    17. free(ch); // 释放内存空间
    18. }
    19. else {
    20. printf("申请内存空间资源异常!");
    21. }
    22. return 0;
    23. }

    运行效果:


    中国魔都北京总舵

    D:\program_file\C++_workspace\ProjectToCallocFunctionMemory\x64\Debug\ProjectToCallocFunctionMemory.exe (进程 14932)已退出,代码为 0。
    要在调试停止时自动关闭控制台,请启用“工具”->“选项”->“调试”->“调试停止时自动关闭控制台”。
    按任意键关闭此窗口. . .



    2.4 reaclloc()函数重新分配内存

    realloc()函数重新分配内存:
    原型如下:
        void *realloc(void *ptr,size_t size);使用该函数同样要包含头文件stdlib.h,其功能是改变ptr指针指向的内存空间大小为
        size字节。设定的size大小可以是任意的,也就是说既可以是原来的数组大,也可以比原来的数值小。返回值是一个指向新地址的指针,
        如果出现错误,则返回NULL;


    1. #include
    2. #include
    3. /*
    4. realloc()函数重新分配内存:
    5. 原型如下:
    6. void *realloc(void *ptr,size_t size);使用该函数同样要包含头文件stdlib.h,其功能是改变ptr指针指向的内存空间大小为
    7. size字节。设定的size大小可以是任意的,也就是说既可以是原来的数组大,也可以比原来的数值小。返回值是一个指向新地址的指针,
    8. 如果出现错误,则返回NULL;
    9. */
    10. int main() {
    11. int *fDouble; // 定义整形指针
    12. char* iInt;
    13. fDouble = (int*)malloc(sizeof(int)); // 给指针变量分配整形的内存空间
    14. printf("%d\n", sizeof(*fDouble));// 输出内存空间的大小
    15. iInt = (char*)realloc(fDouble, sizeof(char)); // 使用该函数该内存空间的大小
    16. printf("%d\n", sizeof(*iInt)); //输出内存空间大小
    17. return 0;
    18. }

    运行效果:


    4
    1

    D:\program_file\C++_workspace\ProjectToReallocMemoryFunction\x64\Debug\ProjectToReallocMemoryFunction.exe (进程 18748)已退出,代码为 0。
    要在调试停止时自动关闭控制台,请启用“工具”->“选项”->“调试”->“调试停止时自动关闭控制台”。
    按任意键关闭此窗口. . .


     


     2.5 释放内存空间

    free()函数的原型如下:
    (1)void free(void *ptr);
    free()函数的功能是释放由指针ptr指向的内存空间,使部分内存空间能被其他变量使用。ptr是最近一次调用
    callo()或malloc()函数时返回的值。free()函数是释放函数。因此必须与分配函数搭配起使用。free()函数无返回值;
    (2)申请内存空间之后,响应内存空间使用完毕后应该及时释放,否则可能存在所谓"内存泄露"。所以通常在使用分配内存
    空间函数之后,要使用free()函数释放内存空间;


    1. #include
    2. #include
    3. #include
    4. /*
    5. free()函数的原型如下:
    6. (1)void free(void *ptr);
    7. free()函数的功能是释放由指针ptr指向的内存空间,使部分内存空间能被其他变量使用。ptr是最近一次调用
    8. callo()或malloc()函数时返回的值。free()函数是释放函数。因此必须与分配函数搭配起使用。free()函数无返回值;
    9. (2)申请内存空间之后,响应内存空间使用完毕后应该及时释放,否则可能存在所谓"内存泄露"。所以通常在使用分配内存
    10. 空间函数之后,要使用free()函数释放内存空间;
    11. */
    12. int main() {
    13. int* pInt; // 整数指针
    14. pInt = (int*)malloc(sizeof(int)); // 分配int类型的变量所占的内存空间
    15. // 使用内存空间,来存储变量
    16. (*pInt) = 1000;
    17. // 将数值进行输出
    18. printf("%d\n", *pInt);
    19. // free 释放内存
    20. free(pInt);
    21. // 将值进行输出
    22. printf("%d\n",*pInt);
    23. }

    运行效果:


    1000
    -572662307

    D:\program_file\C++_workspace\ProjectToFreeMemoryTool\x64\Debug\ProjectToFreeMemoryTool.exe (进程 10024)已退出,代码为 0。
    要在调试停止时自动关闭控制台,请启用“工具”->“选项”->“调试”->“调试停止时自动关闭控制台”。
    按任意键关闭此窗口. . .


    三:C语言中引用内存丢失认知


    内存丢失认知升维
     (1)内存丢失现象
     不进行内存释放会造成"内存泄露",从而会导致系统崩溃;
     (2)内存丢失的原因
     如果不使用free()函数,那么程序可能需要使用100GB内存。这其中包括绝大部分的虚拟内存,而由于虚拟内存的操作需要读写磁盘,
     这样会极大地影响到系统的性能,程序因此可能崩溃;
     例如:
         pOld = (int*)malloc(sizeof(int));
      pNew = (int*)malloc(sizeof(int));
      这两行代码分别表示创建了一块内存空间,并且将内存空间的地址分别传给pOld和
      pNew,此时指针pOld和pNew分别指向两块内存空间。如果进行这样的操作;
      pOld = pNew;
      pOld指针就指向了pNew指向的内存空间地址,这时再进行释放内存操作;
      free(pOld);
      此时释放pOld所指向的内存空间是原来pNew指向的,于是这块内存空间被释放了,但是pOld原来指向的那块内存空间还没有被释放,不过
      因为没有指针指向这块内存空间,所有这块内存空间就造成了丢失。
     (3)内存丢失的解决方案
        在程序中编写malloc()函数分配内存空间时,都对应地写出一个free()函数进行释放,这是一个良好的编程习惯。这不但体现在处理大型程序时的必要性,
     也在一定程度上体现程序优美的风格和健壮性;

  • 相关阅读:
    新老用户该如何选择腾讯云服务器!
    C语言程序设计笔记(浙大翁恺版) 第十一周:结构类型
    Java部署到服务器接口404(本地可以正常访问)
    GitHub:建立仓库,本地上传与更新内容
    Mybatis源码解析(七):Mapper代理原理
    【译】使用保留大小写的查找和替换来保存您的命名方式
    分布式锁设计选型 不可重入锁建议使用ZooKeeper来实现 可重入锁建议使用Redis来实现 分布式锁:ZooKeeper不可重入锁 Java优化建议
    pip install fitz -i https://pypi.doubanio.com/simple/
    Kafka - 3.x Kafka命令行操作
    Extended Choice Parameter拓展参数化构建
  • 原文地址:https://blog.csdn.net/u014635374/article/details/134342454