zoukankan      html  css  js  c++  java
  • 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
        
  • 相关阅读:
    em,pt和px之间的换算
    css中 中文字体(fontfamily)的标准英文名称
    HTML css面试题
    css实现的透明三角形
    JavaScript经典面试题系列
    C++ template 学习笔记(第二章)
    C++ template 学习笔记 (第五章)
    20120906
    C++ template 学习笔记(第十六章) 16.1 命名模版参数
    C++ template 学习笔记(第三,四章)
  • 原文地址:https://www.cnblogs.com/bky-hbq/p/13773879.html
Copyright © 2011-2022 走看看