当前位置: 首页 > article >正文

编译原理复习---运行存储分配

适用于电子科技大学编译原理期末考试复习。

1. 运行存储分配策略

编译器在工作过程中,必须为源程序中出现的一些数据对象分配运行时的存储空间。

对于那些在编译时刻就可以确定大小的数据对象,可以在编译时刻就为它们分配存储空间,这样的分配策略称为静态存储分配

编译时刻分配的存储空间,如何保证在运行起来之后不会与其他程序产生冲突呢(不在编译原理的范畴之内,感兴趣可作了解)?->Linux笔记---进程:进程地址空间-CSDN博客

反之,如果不能在编译时完全确定数据对象的大小,就要采用动态存储分配的策略。即在编译时仅产生各种必要的信息,而在运行时刻,再动态地分配数据对象的存储空间。

动态存储分配包括:栈式存储分配、堆式存储分配。

2. 活动记录

使用过程(或函数、方法)作为用户自定义动作的单元的语言,其编译器通常以过程为单位分配存储空间。过程体的每次执行称为该过程的一个活动(aclivanion)。

过程每执行一次,就为它分配一块连续存储区,用来管理过程一次执行所需的信息,这块连续存储区称为活动记录(activation record)。

一个活动记录通常具有以下几个部分:

  1. 临时变量域:用于存放目标程序临时变量的值,例如在计算表达式时产生的中间结果。

  2. 局部数据域:用于存放过程本次执行中的局部数据。

  3. 机器状态域:用于保存在调用一个过程之前有关机器状态的信息,包括各种寄存器的当前值和返回地址等。

  4. 访问链:为访问其它活动记录中所存放的非局部数据提供链地址(在某些语言中需要用到,如PASCAL语言)。

  5. 控制链:用以指向主调过程的活动记录。

  6. 返回值域:被调用过程用来为主调过程存放返回值的域。

  7. 实在参数:用于存放主调过程为被调用过程所提供的实在参数信息(在活动记录中,列出了实在参数的存放空间,但有时参数是通过机器寄存器来传递的,以提高效率)。

 排序按照活动记录中各部分的相对地址由低到高的顺序。

3. 变量的存储分配

这是PPT独占的内容,其他地方都没看到。

局部数据 x 的地址:

  • D表示活动记录的首地址

  • offset(x): x 在活动记录中的偏移

  • D+offset(x) 为 x 的地址

局部数据的类型:

  • 静态变量:D 和 offset(x) 在编译时都能确定下来。

  • 半静态变量:offset(x) 在编译时能确定下来,D 在运行时能确定下来。

  • 半动态变量:D和offset(x) 在编译时都不能确定下来,但运行能确定。

  • 动态变量:offset(x) 在编译、运行时都不能确定下来,即 x 的大小在运行时不固定,可动态改变。

仅支持静态变量的语言采用的存储分配方式为静态存储分配。 

4. 静态存储分配

在静态存储分配中,编译器为每个过程确定其活动记录在目标程序中的位置。

这样,过程中每个名字的存储位置就确定了,这些名字的存储地址可以被编译到目标代码中。过程每次执行时,它的名字都绑定到同样的存储单元。

适合静态存储分配的语言(即仅支持静态变量)必须满足以下条件:

  • 数组上下界必须是常数

  • 不允许过程的递归调用

  • 不允许动态建立数据实体

 静态存储分配的限制很大,PPT上也没有讨论其具体的分配策略,这里就不再多说。

5. 动态存储分配

  • 栈式存储分配:运行时,每进入一个过程,就在栈顶为该过程分配一块数据区,一旦退出该过程,它所占的空间也退还给系统。

  • 堆式存储分配:有些语言允许用户随时动态地申请和释放存储空间,这种策略是让运行程序持有一个大的存区(堆),在申请时从堆中取一块,释放时将一块存储区退还给堆。

我们主要讨论栈式存储分配,即当一个过程被调用时,该过程的活动记录被压入栈;当过程结束时,该活动记录被弹出栈。

下面这些随便看看,理解以下就行。

 

 6. 非局部数据的访问

这里的讨论要分为两种情况:不支持过程嵌套声明的语言 和 支持过程嵌套声明的语言。

6.1 不支持过程嵌套声明的语言

例如C语言,非重点。

过程中使用的数据,要么是自身定义的局部数据,要么是在所有过程之外定义的全局数据。

 6.2 支持过程嵌套声明的语言

过程嵌套声明是指在过程中定义过程,例如lambda表达式:

#include <iostream>

// 外部函数
void outerFunction() {
    int outerVar = 10;

    // 在外部函数内部定义的内部函数
    auto innerFunction = [&]() {
        // 内部函数可以访问外部函数的变量
        std::cout << "Inner function: outerVar = " << outerVar << std::endl;
    };

    innerFunction();
}

int main() {
    outerFunction();
    return 0;
}
  • 过程的嵌套深度:不内嵌在任何其它过程中的过程,设其嵌套深度为1。如果一个过程p在一个嵌套深度为i的过程中定义,则设定p的嵌套深度为i+1。

  • 变量的嵌套深度:将变量声明所在过程的嵌套深度作为该变量的嵌套深度。

上面的例子中,outerFunction的嵌套深度为1,innerFunction的嵌套深度为2。

 也就是说,一个活动记录 n 的访问链指向的活动记录为:

  1. 直接定义了该活动记录对应的过程的过程的活动记录。

  2. 从栈顶向栈底查找,最早满足条件一的那一个。

7. 总结

重点:活动记录的组成和他们各自的含义,控制链、访问链的画法。

哈工大的慕课中,关于控制链和访问链还有更多的细节,但是根据PPT的内容,我感觉考试顶多就按照下面的方式来考了:

PPT中的栈比较离谱,是向上生长的,蓝色的线代表控制链,红色的线代表访问链,右部的图反应的是各过程的嵌套深度。 


http://www.kler.cn/a/447567.html

相关文章:

  • 详解 Qt WebEngine 模块
  • PHP接入美团联盟推广
  • 武汉市电子信息与通信工程职称公示了
  • 前端小白学习之路-Vben探索 vite 配置 - 1/50
  • C语言编程1.27汉诺塔
  • Restaurants WebAPI(二)——DTO/CQRS
  • 化工行业SAP管理系统:构建未来可持续生产模式的基石
  • 【新立电子】FPC的未来展望:柔性电子技术的无限可能
  • Python数学运算
  • jquery弹性动画特效插件DomLastic.js
  • 基于cobra开发的k8s命令行管理工具k8s-manager
  • Redis篇--常见问题篇9--其他一些问题
  • Coding Caprice - Linked-List 1
  • 【第八节】git与github
  • 【Leecode】Leecode刷题之路第88天之合并两个有序数组
  • Linux下基于最新稳定版ESP-IDF5.3.2开发esp32s3入门任务间的通讯-信号量【入门三】
  • 前端项目打包部署后,如何避免让用户强制去清除浏览器缓存
  • STM32低功耗模式结合看门狗
  • 【RK3588 Linux 5.x 内核编程】-内核中断与ThreadedIRQ
  • 学习Cookie 提升
  • 裸机按键输入实验
  • linux源码编译libunwind
  • 条款34 考虑lambda而非std::bind
  • JS中的innerHTML,innerText,value的区别
  • STM32-笔记5-按键点灯(中断方法)
  • java线程