29 程序中的三个基本数据区:栈、堆、静态存储区

1 程序中的栈

  • 栈在程序中用于维护函数调用上下文

  • 函数中的参数和局部变量存储在栈上

  • 栈示意图

    • esp:ESP(Extended Stack Pointer)为扩展栈指针寄存器,是指针寄存器的一种,用于存放函数栈顶指针。与之对应的是EBP(Extended Base Pointer),扩展基址指针寄存器,也被称为帧指针寄存器,用于存放函数栈底指针。

      ESP为栈指针,用于指向栈的栈顶(下一个压入栈的活动记录的顶部),而EBP为帧指针,指向当前活动记录的底部。

    • 当不需要使用某一块栈内存时,esp 后退,对应着 pop 操作

  • 栈保存了一个函数调用所需的维护信息

    • 参数
    • 返回地址
    • 局部变量
    • 调用上下文
    • 。。。

  • 函数调用过程

    • 每次函数调用都对应着一个栈上的活动记录

      • 调用函数的活动记录位于栈的中部
      • 被调函数的活动记录位于栈的顶部

  • 函数调用的栈变换

    • main() 开始运行

    • main() 调用 f()

    • 当从 f() 调用中返回 main()

  • 函数调用栈上的数据

    • 函数调用时,对应的栈空间在函数返回前是专用的
    • 函数调用结束后,栈空间将被释放,数据不再有效

  • 示例:指向栈数据的指针——错误行为

    • Demo1

      #include <stdio.h>
      
      int* g()
      {
          int a[10] = {0};
          
          return a;  //返回一个局部数组
      }
      
      void f()
      {
          int i = 0;
          int b[10] = {0,1,2,3,4,5,6,7,8,9};
          int* pointer = g();
          
          for(i = 0;i < 10;i++)
          {
              b[i] = pointer[i];
          }
          
          for(i = 0;i < 10;i++)
          {
              printf("%d
      ",b[i]);
          }
      }
      
      int main()
      {
          f();
          
          return 0;
      }
      
    • 编译

      test.c: In function 'g':
      test.c:7: warning: function returns address of local variable
      
    • 运行

      0
      0
      0
      0
      0
      0
      0
      0
      0
      0
      
    • Demo2:验证栈空间的数据在函数调用后会被修改

      #include <stdio.h>
      
      int* g()
      {
          int a[10] = {0};
          
          return a;  //返回一个局部数组
      }
      
      void f()
      {
          int i = 0;
          int b[10] = {0,1,2,3,4,5,6,7,8,9};
          int* pointer = g();
          
          for(i = 0;i < 10;i++)
          {
              printf("%d
      ",pointer[i]);
          }
      }
      
      int main()
      {
          f();
          
          return 0;
      }
      
    • 编译运行

      • 分析:g 函数调用完毕后,其栈空间仍然在那,只是 esp,ebp 指针移动了位置而已。但当下一个函数调用后,即 printf 函数调用后,需要在栈上建立一个新的记录,原先 g 函数的栈空间的数据会因此发生变化,也就是说原先的活动记录被销毁了。
      0 
      12976116
      0
      0
      -1081184744
      11856928
      -1081184664
      11856928
      12977376
      134514000
      

2 程序中的堆

  • 堆是程序中一块预留的内存空间,可由程序自由使用

  • 堆中被程序申请使用的内存在被主动释放前将一直有效

  • 问题:为什么有了栈还需要堆?

    • 栈上的数据在函数返回后就会被释放掉,无法传递到函数外部,如:局部数组
  • C 语言程序中通过库函数的调用获得堆空间

    • 头文件:malloc.h
    • malloc :以字节的方式动态申请堆空间
    • free :将堆空间归还给系统
  • 程序对堆空间的管理方式

    • 空闲链表法,位图法,对象池法等

    • malloc 返回的内存大小可能会比申请的要大一点,因为以空闲链表法管理堆空间时,寻找到的是最接近的那个空间,一般都是大于等于所需的内存大小

3 程序中的静态存储区

  • 静态存储区随着程序的运行而分配空间

  • 静态存储区的生命周期直到程序运行结束

  • 在程序的编译期静态存储区的大小就已经确定

  • 静态存储区主要用于保存全局变量和静态局部变量

  • 静态存储区的信息最终会保存到可执行程序中(.exe 或者 .out 文件中)

  • 示例:静态存储区的验证

    • Demo

      #include <stdio.h>
      
      //全局变量
      int g_v = 1;
      
      //静态全局变量:只在当前文件中可见
      static int g_vs  = 2;
      
      void f()
      {
          //静态局部变量
          static int g_vl = 3;
          
          printf("%p
      ", &g_vl);
      }
      
      int main()
      {
          printf("%p
      ", &g_v);
          
          printf("%p
      ", &g_vs);
          
          f();
          
          return 0;
      }
      
    • 编译运行:可以发现,这三个变量的地址是连续的,都是存放在程序的静态存储区

      0x804a014
      0x804a018
      0x804a01c
      
原文地址:https://www.cnblogs.com/bky-hbq/p/13773879.html