鱼C论坛

 找回密码
 立即注册
查看: 2176|回复: 0

[学习笔记] 004-内存四区模型

[复制链接]
发表于 2018-8-2 21:16:16 | 显示全部楼层 |阅读模式

马上注册,结交更多好友,享用更多功能^_^

您需要 登录 才可以下载或查看,没有账号?立即注册

x
本帖最后由 moc 于 2018-8-3 21:50 编辑

1、内存四区模型
      理解内存四区模型图,理解变量在内存中存储方式,深入理解程序的运行过程。
未命名文件.jpg
流程说明
1、操作系统把物理硬盘代码load到内存
2、操作系统找到main函数入口执行
3、操作系统开辟四个区,进行执行过程中的内存管理
2、内存四区
1、代码区
       代码区code,程序被操作系统加载到内存的时候,所有的可执行代码都加载到代码区,也叫代码段,这块内存是不可以在运行期间修改的。
2、全局区(静态区)(static)
       全局变量和静态变量的存储是放在一块的,初始化的全局变量和静态变量在一块区域,未初始化的全局变量和未初始化的静态变量在相邻的另一块区域,该区域在程序结束后由操作系统释放。
常量区,(也归为静态区)字符串常量和其他常量的存储位置,程序结束后由操作系统释放。
3、栈区(stack)
       栈是用户存放程序临时创建的局部变量,也就是我们函数大括号"{}"中定义的变量(不包括static声明的变量)。除此以外,在函数被调用时,其参数也会被压入发起调用的进程栈中,并且等调用结束后,函数的返回值也会被存放在回栈中。由于栈的先进先出特性,所有栈特别方便用来保存/恢复调用现场。从这个意义上讲,把堆栈看成一个寄存、交换临时数据的内存区。由编译器自动分配释放,存放函数的参数值,局部变量的值等。其操作方式类似于数据结构中的栈。
       在被调函数返回时,在运行该函数过程中存储在栈区中的数据会被销毁,而放入其他区的数据则不会。
4、堆(heap)
      堆是用于存放进程运行中被动态分配的内存段,它的大小并不固定,可动态扩张或缩减。当进程调用malloc等函数分配内存时,新分配的内存就被动态添加到堆上(堆被扩张);当利用free等函数释放内存时,被释放的内存从堆上被剔除(堆被缩减)。一般由程序员分配释放(new/malloc/calloc delete/free),若程序员不释放,程序结束时可能由 OS 回收。注意:它与数据结构中的堆是两回事,但分配方式类似于链表。
        ①只读数据段(RO data,.rodata):只读数据段是程序使用的一些不会被改变的数据,使用这些数据的方式类似查表式的操作,由于这些变量不需要修改,因此只需放在只读存储器中。
已初始化读写数据段(data segment,.data段):通常是用来存放程序中已初始化的全局变量的一块内存区域。数据段属于静态内存分配。常量字符串就是放在这里的,程序结束后由系统释放(rodata—read only data)。已初始化读写数据段(RW data,.data):已初始化数据是在程序中声明,并且具有初值的变量,这些变量需要占用存储器空间,在程序执行时它们需要位于可读写的内存区域,并具有初值,以供程序读写。      
                *只读数据段和数据段统称为 数据段(data)
        ②BSS段(bss segment,.bss段):未初始化数据段(BSS,.bss)通常是指用来存放程序中未初始化的全局变量的一块内存区域。BSS是英文Block Started by Symbol的简称。BSS段属于静态内存分配。全局变量 和 静态变量 的存储是放在一块的。初始化的全局变量和静态变量在一块区域(.rwdata or .data),未初始化的全局变量和未初始化的静态变量在相邻的另一块区域(.bss), 程序结束后由系统释放。未初始化数据是在程序中声明,但是不具有初值的变量,这些变量在程序运行之前不需要占用存储空间。
                * 在 C++中,已经不再严格区分bss和 data了,它们共享一块内存区域
                * 静态存储区包括bbs段和data段

3、堆栈的生长方
        一般来说,栈的生长方向是向下的;堆的生长方向是向上;但我在微软visual C++ 2015 x64下尝试,其栈的方向向上?详见传送门
360截图20180802210354309.jpg
4、代码示例
1、示例1
内存四区

  1. int a = 0;      // a 在 data  全局初始化区
  2. char *p1;       // p1 在 bss  全局未初始化

  3. main()
  4. {
  5.     int b;                      // b 在 stack
  6.     char s[] = "abc";           // s 在 stack, 字符串abc\0 在常量区
  7.     char *p2;                   // p2 在 stack
  8.     char *p3 = "123456";        // p3 在 stack, 字符串123456\0 在常量区
  9.     static int c = 0;           // c 在 data   全局静态初始化区
  10.     p1 = (char *)malloc(10);    // 申请的10字节内存在 heap, bss中的指针指向heap中的内存
  11.     p2 = (char *)malloc(20);    // 申请的20字节内存在 heap, stack中的指针指向heap中的内存
  12.     strcpy(p1, "123456");       // 123456\0 在常量区,编译器可能会将它与 p3 所指向的 "123456\0" 优化成一块
  13. }
复制代码

1、示例2
堆的注意事项
  1. #include <stdio.h>
  2. #include <stdlib.h>
  3.   
  4. int *geta() // 错误,不能将一个栈变量的地址通过函数的返回值返回
  5. {
  6.   int a = 0;
  7.   return &a;
  8. }
  9.   
  10. int *geta1() // 可以通过函数的返回值返回一个堆地址,但记得,一定要free
  11. {
  12.   int *p = (int *)malloc(sizeof(int)); // 申请了一个堆空间
  13.   return p;
  14. }
  15.   
  16. int *geta2() // 合法的,但是记住这里不能用free
  17. {
  18.   static int a = 0; // 变量在静态区,程序运行过程中一直存在
  19.   return &a;
  20. }
  21.   
  22. void getHeap(int *p)
  23. {
  24.   printf("p = %p\n", &p);
  25.   p = (int *)malloc(sizeof(int) * 10);
  26. } // getHeap执行完之后,p就消失了,导致他指向的具体堆空间的地址编号也随之消失了
  27. // 这里发生了内存泄漏
  28.   
  29. void getHeap1(int **p)
  30. {
  31.   *p = (int *)malloc(sizeof(int) * 10);
  32. } // 这里的操作就是正确的
  33.   
  34. int main()
  35. {
  36.   int *p = NULL;
  37.   printf("p = %p\n", &p);
  38.   getHeap(p); // 实参没有任何改变
  39.   getHeap1(&p); // 得到了堆内存的地址
  40.   printf("p = %d\n", p);
  41.   
  42.   p[0] = 1;
  43.   p[1] = 2;
  44.   printf("p[0] = %d, p[1] = %d\n", p[0], p[1]);
  45.   free(p);
  46.   
  47.   return 0;
  48. }
复制代码


5、函数调用模型
360截图20180803214618266.jpg

       main函数调用函数fa(),在函数fa()内再调用函数fb(),其内存状态:
360截图20180803214543075.jpg

本帖被以下淘专辑推荐:

小甲鱼最新课程 -> https://ilovefishc.com
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

小黑屋|手机版|Archiver|鱼C工作室 ( 粤ICP备18085999号-1 | 粤公网安备 44051102000585号)

GMT+8, 2025-6-21 20:21

Powered by Discuz! X3.4

© 2001-2023 Discuz! Team.

快速回复 返回顶部 返回列表