|
马上注册,结交更多好友,享用更多功能^_^
您需要 登录 才可以下载或查看,没有账号?立即注册
x
本帖最后由 moc 于 2018-8-3 21:50 编辑
1、内存四区模型
理解内存四区模型图,理解变量在内存中存储方式,深入理解程序的运行过程。
流程说明
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下尝试,其栈的方向向上?详见传送门
4、代码示例
1、示例1
内存四区
- int a = 0; // a 在 data 全局初始化区
- char *p1; // p1 在 bss 全局未初始化
-
- main()
- {
- int b; // b 在 stack
- char s[] = "abc"; // s 在 stack, 字符串abc\0 在常量区
- char *p2; // p2 在 stack
- char *p3 = "123456"; // p3 在 stack, 字符串123456\0 在常量区
- static int c = 0; // c 在 data 全局静态初始化区
- p1 = (char *)malloc(10); // 申请的10字节内存在 heap, bss中的指针指向heap中的内存
- p2 = (char *)malloc(20); // 申请的20字节内存在 heap, stack中的指针指向heap中的内存
- strcpy(p1, "123456"); // 123456\0 在常量区,编译器可能会将它与 p3 所指向的 "123456\0" 优化成一块
- }
复制代码
1、示例2
堆的注意事项
- #include <stdio.h>
- #include <stdlib.h>
-
- int *geta() // 错误,不能将一个栈变量的地址通过函数的返回值返回
- {
- int a = 0;
- return &a;
- }
-
- int *geta1() // 可以通过函数的返回值返回一个堆地址,但记得,一定要free
- {
- int *p = (int *)malloc(sizeof(int)); // 申请了一个堆空间
- return p;
- }
-
- int *geta2() // 合法的,但是记住这里不能用free
- {
- static int a = 0; // 变量在静态区,程序运行过程中一直存在
- return &a;
- }
-
- void getHeap(int *p)
- {
- printf("p = %p\n", &p);
- p = (int *)malloc(sizeof(int) * 10);
- } // getHeap执行完之后,p就消失了,导致他指向的具体堆空间的地址编号也随之消失了
- // 这里发生了内存泄漏
-
- void getHeap1(int **p)
- {
- *p = (int *)malloc(sizeof(int) * 10);
- } // 这里的操作就是正确的
-
- int main()
- {
- int *p = NULL;
- printf("p = %p\n", &p);
- getHeap(p); // 实参没有任何改变
- getHeap1(&p); // 得到了堆内存的地址
- printf("p = %d\n", p);
-
- p[0] = 1;
- p[1] = 2;
- printf("p[0] = %d, p[1] = %d\n", p[0], p[1]);
- free(p);
-
- return 0;
- }
复制代码
5、函数调用模型
main函数调用函数fa(),在函数fa()内再调用函数fb(),其内存状态:
|
|