内存四区的建立流程

内存四区模型 - 图1

流程说明

  1. 操作系统把物理硬盘代码load到内存
  2. 操作系统把c代码分成四个区
  3. 操作系统找到main函数入口执行

各区元素分析

栈区(stack)

—由编译器自动分配释放 ,存放函数的参数值,局部变量的值等。其操作方式类似于数据结构中的栈。

堆区(heap)

一般由程序员分配和释放,若程序员不释放,程序运行结束时由操作系统回收。

malloc()、calloc()、free() 等函数操作的就是这块内存,这也是本章要讲解的重点。

注意:这里所说的堆区与数据结构中的堆不是一个概念,堆区的分配方式倒是类似于链表。

静态数据区(data area):

也称全局数据区,包含的数据类型比较多,如全局变量、静态变量、一般常量、字符串常量。

其中:

  • 全局变量和静态变量的存储是放在一块的,初始化的全局变量和静态变量在一块区域
  • 未初始化的全局变量和未初始化的静态变量在相邻的另一块区域。
  • 常量数据(一般常量、字符串常量)存放在另一个区域。
    注意:静态数据区的内存在程序结束后由操作系统释放

程序代码区

存放函数体的二进制代码

命令行参数区

存放命令行参数和环境变量的值,如通过main()函数传递的值

内存四区模型 - 图2

  1. #include<stdio.h>
  2. #include<stdlib.h>
  3. #include<string.h>
  4. int a = 0; // 全局初始化区(④区)
  5. char *p1; // 全局未初始化区(③区)
  6. int main()
  7. {
  8. int b; // 栈区
  9. char s[] = "abc"; // 栈区
  10. char *p2; // 栈区
  11. char *p3 = "123456"; // 123456\0 在常量区(②),p3在栈上,体会与 char s[]="abc"; 的不同
  12. static int c = 0; // 全局初始化区
  13. p1 = (char *)malloc(10), // 堆区
  14. p2 = (char *)malloc(20); // 堆区
  15. // 123456\0 放在常量区,但编译器可能会将它与p3所指向的"123456"优化成一个地方
  16. strcpy(p1, "123456");
  17. }

内存分配方式有三种

  1. 从静态存储区域分配。内存在程序编译的时候就已经分配好,这块内存在程序的整个运行期间都存在。例如全局变量,static变量。
  2. 在栈上创建。在执行函数时,函数内局部变量的存储单元都可以在栈上创建,函数执行结束时这些存储单元自动被释放。
    栈内存分配运算内置于处理器的指令集中,效率很高,但是分配的内存容量有限。
  3. 从堆上分配,亦称动态内存分配。程序在运行的时候用malloc或new申请任意多少的内存,程序员自己负责在何时用free或delete释放内存。
    动态内存的生存期由我们决定,使用非常灵活,但问题也最多。
  4. 一般所说的堆栈(stack)往往是指栈,先进后出,它是一块内存区。
    用以存放程序的局部变量,临时变量,函数的参数,返回地址等。
    在这块区域中的变量的分配和释放由系统自动进行。不需要用户的参与。
    而在堆(heap,先进先出)上的空间则是由用户进行分配,并由用户负责释放。
  1. #include<stdio.h>
  2. #include<stdlib.h>
  3. //程序代码指令,define定义的常量---代码区(只读)
  4. //全局(关键)变量/常量,静态(关键)变量/常量,常量字符串---静态区
  5. //静态区的内容在总个程序的生命周期内都存在,由编译器在编译的时候分配。
  6. //局部变量---栈区
  7. //栈区上的内容只在函数的范围内存在,当函数运行结束,这些内容也会自动被销毁。其特点是效率高,但空间大小有限。
  8. //由malloc或者realoc,calloc分配的内存区域---堆区
  9. //堆区其生命周期由free决定。
  10. #define PI 3.14 //
  11. //全局变量
  12. int a = 10;
  13. int b[5] = { 1, 2, 3, 4, 5 };
  14. int const c = 2;
  15. void run2();
  16. void main(){
  17. int num = 10;
  18. //对于int num = 10;的理解
  19. //int 数据类型 编译器预算对象(变量)num分配4个字节内存空间大小
  20. //num是内存中一段内存空间的标识(相当于门牌号),
  21. //10 才是存储在这段内存空间里的数据
  22. //详细的来说,“int num=10;”这段字符存储在代码区,数据10存储在栈区
  23. //验证局部常量是否存储在栈区
  24. run2();
  25. //经过内存观察,开始局部常量cde的内存空间的数据是2
  26. //当run2()函数执行完毕,局部常量cde的内存空间的数据发生变化
  27. //因为静态区的内容在总个程序的生命周期内都存在,所以判断局部常量存储在栈区
  28. char *p = "asdfadsf";//"asdfadsf"是字符串常量,在静态区,只读
  29. //指针p是临时变量,在栈区
  30. //验证 "asdfadsf" 只读
  31. //*p = 'a';//编译不报错
  32. printf("%s\n", p);//运行报错--“写入位置 0x001B692C 时发生访问冲突。”,由此证明"asdfadsf"是字符串常量,只读
  33. //堆只能是malloc或者realoc,calloc,分配的内存才是堆
  34. //例如以下代码
  35. void *p1 = malloc(sizeof(int)* 1024);//malloc(sizeof(int)* 1024);分配的内存就是堆区,但是指针p1是在栈区
  36. //堆的使用情况:①无法确定需要内存的大小;②需要的内存很大
  37. //堆的注意点:堆和栈不同,堆的内存大小是由程序员分配的,必须由程序员手动释放(free(p1););
  38. //对于栈来说,栈的内存大小由系统分配(栈的极限大约是1M,非常小),所以栈的内存空间是由系统回收的
  39. //堆的内存大小是由程序员分配的,理论上可以占据系统中所有的内存(甚至能抢占栈的内存空间)
  40. //因为栈的内存空间是由系统回收的,所有在回收的时候会消耗大量的cpu
  41. char d = 'a';//变量d的数据在栈区中
  42. system("pause");
  43. }
  44. //run1是程序代码,所以存储在代码区
  45. void run1(){
  46. while (1){
  47. int x = 0;//x变量的内存空间在栈区,因为while循环,x每次循环都会初始化,所以系统不断的在栈上创建x的内存空间,回收x的内存空间,会消耗大量的cpu
  48. printf("%d", x);
  49. }
  50. }
  51. void run2(){
  52. const cde = 2;//变量cde的数据在栈区,与cde是否是常量没关系
  53. printf("%x\n",&cde);//获取cde的地址,检测run2()函数在main()执行完成之后,系统是否会回收cde指向的内存空间
  54. }
  55. int add(int a, int b){//形参a,b是临时变量,形参a,b的数据存储在栈区
  56. return a + b;//a+b的操作会在寄存器中执行
  57. }