程序设计语言的复习笔记

版权声明:本文为博主原创,未经博主允许不得转载。 https://blog.csdn.net/weixin_36904568/article/details/89716660

一:基本概念

1. 计算机系统的语言

  • 低级语言
    • 机器语言:由0、1组成的机器指令序列
    • 汇编语言:用符号表示的指令的集合
  • 高级语言
    • 程序设计语言:与自然语言接近

2. 语言处理程序

(1)定义

  • 源程序:高级程序设计语言或汇编语言编写的程序
  • 可执行程序:计算机理解的机器语言编写的程序
  • 语言处理程序:将源程序翻译为可执行程序

(2)种类

  • 汇编语言:需要汇编程序翻译
  • 高级语言:需要解释程序或编译程序翻译
1:解释程序
  • 直接解释并执行源程序
  • 翻译为中间代码再执行

特点:

  • 不生成独立的目标程序
  • 需要反复扫描,效率低
  • 灵活
  • 可移植
2:编译程序

将源程序翻译成目标程序,并执行目标程序

特点:

  • 生成独立保存的目标程序
  • 效率高
  • 不灵活

3. 程序设计语言的定义

  • 语法:程序设计语言的基本符号组成各个语法成分的一组规则,用形式语言描述
    • 词法规则:由基本字符构成的单词书写规则
    • 语法规则:由符号构成语法成分的规则
  • 语义:程序设计语言按语法规则构成的各个语法成分的含义,程序的执行结果说明了其语义
    • 静态语义:编译时可以确定语法成分的含义
    • 动态语义:运行时刻才能确定的语义
  • 语用:构成语言的各个记号和使用者的关系
  • 语境:理解和实现程序设计语言的环境

4. 程序设计语言的发展

  • Fortran:第一个用来进行科学和工程计算的高级语言,解决数学公式的自然描述
  • ALGOL
  • PASCAL:过程式结构化语言
  • C语言:通用程序设计语言,提供高效的执行语句,可以直接访问操作系统和底层硬件
  • C++:面向对象,增加了封装、抽象、类
  • C+:面向对象的高级程序设计语言
  • Objective-C:面向对象,可以在gcc运作的系统中编译编写
  • JAVA:通用程序设计语言
  • Ruby:解释性、面向对象、动态类型的脚本语言,一切皆对象
  • PHP:在服务器端运行的,嵌入HTML的脚本语言,充分利用服务器性能
  • Python:面向对象的解释型语言,支持对操作系统的底层访问,也可以编译成字节码在虚拟机上运行
  • JavaScript:嵌入HTML的脚本语言
  • Delphi:可视化开发工具,基于窗体和面向对象的方法
  • Visul Basic .NET:面向对象,源代码编译为中间代码MSIL后通过 .NET Framework的CLR执行

5. 程序设计语言的分类

(1)命令式和结构化(C、PASCAL)

  • 命令式:基于动作,把计算过程当做动作的序列
  • 结构化
    • 自顶向下逐步精化
    • 模块组织
    • 程序只包含顺序、分支、循环,且每种构造只允许单入口和单出口

特点:结构清晰,模块性强、可读性强

(2)面向对象(JAVA、C++、Smlltalk)

支持数据隐藏、数据抽象、继承、多态、用户自定义类型等新功能,不断抽象

(3)函数式(LISP、Haskell、Scala)

把函数当做函数

F1(n)=…
F2(n)=F1(F1(n))
F3(n)=F3(n-1)…

  • 输入:函数的自变量
  • 输出:将输入通过递归或嵌套其他函数组合起来

特点:表达式中的函数都可以用其他函数代替

(4)逻辑型(PROLOG)

以形式逻辑为基础,是一系列事实,数据对象,具体关系和规则的集合

断言 P:-P1,P2…PN(若P1,P2…为真,则P为真)
事实 P.

  • 通过查询操作把事实和规则输入数据库
  • 用户通过查询,执行程序,进行模式匹配

特点:适用于编写定理证明、专家系统、自然语言理解问题

6. 程序设计语言的成分

(1)数据成分

指出程序的数据类型——代表数据对象,完成值的布局,检查运算的应用是否正确

数据的定义

数据是程序操作的对象,在使用时需要分配内存空间

  • 数据的名称:用户通过标识符命名
  • 数据的类型:数据占用内存的大小和存放的方式
  • 数据的存储类别:数据在内存的位置和生存期
  • 数据的作用域:使用数据的代码范围
数据的分类
  • 数据的值
    • 常量:只具有右值(内容),且值不变
    • 变量 :具有左值(存储单元)和右值(内容),且值可以改变
  • 数据的作用范围
    • 全局量:整个文件或程序。存储空间不会改变
    • 局部量:一个函数或语句块。存储单元动态改变
  • 数据的组织形式
    • 基本类型
    • 用户自定义类型
    • 构造类型
    • 其他类型

(2)运算成分

指出程序允许使用的运算符号和运算规则

  • 算术运算
  • 关系运算
  • 逻辑运算

(3)控制成分

指出程序的控制结构

  • 顺序结构:表示一个计算操作序列
  • 选择结构:在多个分支中选择一个
  • 循环结构:初始化,根据条件循环

(4)传输成分

指出程序允许的数据传输方式

  • 赋值处理
  • 数据输入
  • 数据输出

(5)函数

结构
  • 函数首部
    • 返回值的数据类型
    • 函数名
    • 参数及其类型
  • 函数体
使用

函数需要先声明,再引用。

  • 值调用:将实参的值传递给形参
  • 引用调用:当形参为引用时,对形参的修改即为对实参的修改

二:语言处理程序

1. 汇编程序

(1)汇编语言的源程序

由若干条指令语句组成

  • 指令语句:也叫机器指令语句,汇编后产生机器代码(可以被CPU直接识别),在程序运行时完成指定的操作
    • 传送指令
    • 算术运算指令
    • 逻辑运算指令
    • 移位指令
    • 转移指令
    • 处理机控制指令
  • 伪指令语句:汇编后不会产生机器代码,需要在汇编的时候就完成指定的工作
  • 宏指令语句:将多次使用的程序定义为宏,每个宏都有宏名,可以在任意位置引用。

(2)汇编程序的过程

将每一条可执行汇编语句转换成对应的机器指令,处理伪指令,一般需要两次扫描。

第一次扫描——记录符号和符号值
  • 设立符号表ST
  • 使用机器指令表MOT1,查找机器指令的记忆码和指令长度
  • 设立一个位置(单元地址)计数器LC,每处理完一条机器指令或源指令,LC增加相应的长度,表示下一条被汇编的指令的偏移地址
  • 设立伪指令表POT1,记录伪指令助记符和相应的子程序入口
  1. LC初始化为0
  2. 打开源程序文件,读入语句
    1. 当前语句有标号:将标号和LC的值记录在ST中
    2. 当前语句是可执行的汇编指令语句:查找MOT1,获取指令长度K,LC=LC+K
    3. 当前语句是伪指令:查找POT1,调用子程序——在汇编时处理
    4. 当前语句的操作码是非法记忆码:调用出错处理子程序
    5. 继续读下一条,直到END语句
  3. 关闭源程序文件
第二次扫描——产生目标程序
  • 第一次扫描的符号表ST
  • 使用机器指令表MOT2,查找机器指令助记符和机器指令二进制操作码、格式、长度
  • 设立伪指令表POT2,记录伪指令记忆码和相应的子程序入口
  1. 打开源程序文件,读入语句
    1. 当前语句是可执行的汇编指令语句:查找MOT2,把机器指令助记符转换为二进制机器指令操作码
    2. 求出操作数区各个操作数的值,用二进制表示。如果出现符号,则通过查找ST获取其对应的指令地址
    3. 当前语句是伪指令:查找POT2,调用子程序
    4. 当前语句的操作码是非法记忆码:调用出错处理子程序
    5. 继续读下一条,直到END语句
  2. 关闭源程序文件

2. 编译程序

前端——与机器无关

(1)词法分析

基本概念
1:字符串
  • 字母表∑:字母表是字符的非空有穷集合
  • 字符串:字母表∑中的字符组成的有穷序列
    • 长度:字符的个数
    • 方幂:将自身连接N次 A0
  • 空串ε:由0个字符组成的序列
  • 连接:字符串S和T的连接,记为S * T,S * ε=ε * S=S
  • 字符表的所有字符串∑ *(包含空串)
  • 集合运算:
    • 或(A∪B):字符串集合A或B的字符串X
    • 积(AB):字符串集合A和B共同字符串X
    • 幂(An):字符串集合的自连接N次,A0
    • 正则闭包(A+):A1∪A:2∪…∪An∪…
    • 闭包(A*):A0∪A+
2:文法

描述语言语法结构的规则,用四元组表示:G=(VN,VT,P,S)

  • 词汇表V:V中的元素为文法的符号
    • 非终结符集VN:非空有限集,每个元素为非终结符
    • 终结符集VT:非空有限集,每个元素为终结符
  • 产生式集合P:有限集,每个产生式为一个规则。如A→B(A∈V+,B∈V*),A包含至少一个非终结符。A→B1,A→B2 => A→B1|B2(Bi为A的候选式)
  • 开始符号S:至少要在一条产生式中作为左部出现
分类:
  • 0型文法
    • 短语文法,递归可枚举
  • 1型文法:G的任意产生式A→B,满足A的长度≤B的长度
    • 上下文有关文法 ,替换非终结符需要考虑上下文
  • 2型文法:G的任意产生式A→B,满足A∈VN
    • 上下文无关文法:替换非终结符不需要考虑上下文
  • 3型文法:G的任意产生式A→a或A→aB,满足a∈VT,A和B∈VN
    • 正规文法/线性文法
3:推导和规约
推导

推导:从文法G的开始符号S出发,反复使用产生式,替换左部的非终结符为右部的文法符号序列,直到产生一个终结符的序列

直接推导:对于A→B,存在xAy => xBy(x,y∈V*

  • 对于P的每个产生式都有A => B
  • 若文法中存在推导序列A0=>A1=>…=>AN,则A0可以推导出AN,表示为A0=+=>AN,(A0=*=>AN表示A0=+=>AN和A0=A0)
规约

规约:若文法G存在x =*=> y,则y可规约为x,x是y的一个规约

直接规约:若文法存在直接推导A => B,则B可直接规约为A,A是B的一个直接规约

4:句型、句子、语言
  • 句型:从开始符号S推导出的符号串为文法的一个句型
  • 句子:仅含终结符的句型是一个句子
  • 语言:有限字母表∑上的有限字符串的集合,即从S出发能推导出的全体句子,记为L(G)(若L(G1)=L(G2),则G1和G2等价)
词法分析

对源程序逐个字符扫描,识别“单词”符号,把字符串转换为单词符号序列,一般将“单词”以二元组的方式输出:(单词种别,单词自身的值)。词法规则使用3型文法或正规表达式表示,

  • 关键字:保留
  • 运算符:保留
  • 分隔符:保留
  • 标识符:(用户标识符id,单词值)
  • 常数:(数据类型,常数值)
1:正规式和正规集
正规式的概念

字母表∑中的每个字符通过或|、连接、闭包*进行连接而成的表达式为正规式

正规集的概念

若∑={A,B}

  • L={A}:存在正规式A,或者A是∑上的字符

    • A:字符串A构成的集合 A*:多个A构成的集合
    • B:字符串B构成的集合 B*:多个B构成的集合
    • AB:字符串AB构成的集合 (AB)*:多个AB构成的集合
    • A|B:字符串A或B构成的集合 (A|B)*:多个A或B构成的集合
    • A(AB)*:以A开头的AB的集合 (AB)*A:以A结尾的AB的集合
    • A(A|B)*:以A开头的A或B的集合 (A|B)*A:以A结尾的A或B的集合
  • (L(A))*:正规式A的闭包A*也是正规式,则正规集的闭包也是正规集

  • L(A)∪L(B):正规集的并集也是正规集

  • L(A)*L(B):正规集的交集也是正规集

正规式的性质
  • 等价:若两个正规式的正规集相同,则两个正规式等价 U=V
  • 交换律:U|V=V|U
  • 结合律:U|V |W=U| V|W,UV W=U VW
  • 分配律:(U|V)W=UW|VW
  • 闭包:V*=V+ | ε,V**=V*
2:有限自动机——准确识别正规集
确定的有限自动机DFA

组成:

  • 有限集合S
  • 有穷字母表∑
  • 单值映像F,F(A,X)=B表示当前状态为A,输入X时转换到下一状态B,B为A的后继状态
  • S0:唯一开始状态
  • Z:非空的终止状态集合

表示:

  • 状态转换图:每个状态对应一个结点,双圈表示终止结点,转换过程对应有向弧。
    • 若存在一条路径从起点指向终点,且转换过程的字符串s属于字母表,则s可由该DFA识别
      -若起点和终点相同,则空字符串可被DFA识别
  • 状态转换矩阵:行表示状态,列表示转换过程
不确定的有限自动机NFA

特点:

  • 对于给定的状态和和转换字符,只能确定一个字符集合
  • 转换字符可以是空串
  • DFA是NFA的特例,对于任意一个NFA,都存在一个DFA
NFA→DFA

若A是NFA的状态集合的子集,则A的ε_闭包(A)就是从A的状态出发,经过ε到达的状态集合

若a是∑的一个字符,则Aa=ε_闭包(J)(其中J表示从A的状态出发,经过a到达的状态集合)

子集法:若NFA=(S,∑,F,S0,Z),DFA=(s,∑,f,s0,z)

  1. 求出DFA的初始状态s0,:根据NFA的初始状态,s0=ε_闭包(S0)
  2. 此时s中仅含有未标记的s0,对于s中未标记的状态集si
    1. 标记si,表示开始处理
    2. 对于每个字母表∑的字符a,求出si通过字符a到达的状态集合T=F(si,a)
    3. 求出si的闭包——sj=ε_闭包(T)
    4. 若sj是终点,则作为新的状态并且是终点加入到s,f(si,a)=sj,进行第6步
    5. 若sj不在s中,则将其作为未标记的新状态加入到s,f(si,a)=sj
    6. 重复进行,直到s中全部状态已标记
  3. 此时求出s,则z={q|q∈s,且q∩z=∅}
DFA最小化

可区分字符串:

  • 若从状态A出发接受输入的字符串s,而另一个状态B不接受s
  • 从状态A和B出发到达不同的接受状态

对于不可区分的状态(两个状态可以通过同一个字符串X到达同一个终点),可以合并为等价状态(去掉其中一个状态)

NFA→正规式
  1. 在NFA的状态转换图上加入两个结点。结点A到起点s0引一条弧并用ε标记,从所有终点到结点B引一条弧并用ε标记,此时图上只有1个起点A和1个终点B
  2. 除去除了AB之外的所有结点,用正规式代替弧
    1. 连接:1→x→2→y→3 ——> 1→xy→3
    2. 或:1→x→2,1→y→2 ——> 1→x|y→2
    3. 闭包:1→x→2→y→3,2→z→2 ——> 1→xz*y→3
正规式→NFA
  1. 对于正规式R,扩展状态转换图,加上起点A和终点B,弧用R标记
  2. 通过对R的分裂生成新结点,将弧的标记为字符或ε
    1. 连接:1→xy→3 ——> 1→x→2→y→3
    2. 或: 1→x|y→2 ——> 1→x→2,1→y→2
    3. 闭包: 1→xz*y→3 ——> 1→x→2→y→3,2→z→2
3:构造词法分析器
  1. 用正规式描述语言的单词构成规则
  2. 为正规式构造NFA,识别正规式表示的正规集
  3. 将NFA转换为DFA
  4. 最小化DFA
  5. 通过DFA构造词法分析器

(2)语法分析

根据语法规则,将单词符号序列分解为各类语法单位,如果源程序没有语法错误,则可以正确构造出语法树

  • 表达式
  • 语句
  • 程序
1:上下文无关文法的概念

文法G的任意产生式A→B,满足A∈VN

规范推导(最右推导):如果在推导的任何一步A => B都是对A的最右或最左的非终结符进行替换,则推导为最右或最左推导

短语:若AxB、AyB是文法G的句型,并且y可以推导出x,则x是句型AxB相对于非终结符y的短语

  • 如果y可以直接推导出x,则x是直接短语。一个句型最左直接短语为该句型的句柄
2:自顶向下的语法分析

对于给定的字符串X,从一个文法的开始符号S出发,进行最左推导并建立语法树,直到得到一个合法的句子或发现一个非法结构。

解决的问题:
  • 递归问题:若文法中存在A→Aa,可能会陷入死循环,需要消除左递归
    • 消除直接左递归:改变非终结符Vn和产生式P,成为新的文法
      • 若A→Aa|b,则A→bA’、A‘→aA’|ε
      • 若A→Aa1a2…|b1b2…,则A→b1A’|b2A’…、A‘→a1A’|a2A’|…ε
    • 消除全部左递归:先将产生式的右部进行替换,把非直接左递归转换为直接左递归,再进行消除
  • 回溯问题:若文法中存在A→ab|ac,即产生式有多个候选项的前缀相同,会导致回溯,需要提取左因子
    • A→ab1|ab2|…,则A→aA’、A’→b1|b2…
    • 反复提取,直到每个非终结符的任意两个候选式不含有公共前缀
LL(1)文法

对于文法G的任意两个候选式A→a|b满足条件:

  • 对于任意终结符a,a和b不能同时推导出以a开始的文法序列
  • a和b最多只有一个可以推导出ε
  • 若b是ε的一个规约,则a不能推导出以FOLLOW(A)终结符开始的任何文法符号序列

文法符号序列a的FIRST集合:从a出发可以推导出所有以终结符号开头的序列,其中所有开头终结符号构成的集合。FIRST(a)={x|a=*=>x,x∈Vt}

  • 若文法符号X是终结符,则X加入集合
  • 否则看它的产生式
    • 若X→ε,则ε加入集合
    • 若X→Y,并且Y是终结符,则Y加入集合
    • 若X→Y1Y2…,并且Y1是非终结符,则把FIRST(Y1)加入集合。
      若ε是Y1Y2…的一个规约,则把FIRST(Yi)加入集合

A的FOLLOW集合:从开始符号出发推导出的所有含A的句型中,紧跟在A之后的终结符构成的集合。FOLLOW(A)={a|S=*=>Aa,a∈Vt}

  • 若W文法符号A是开始符号,则#加入集合FOLLOW(A)
  • 若B→aAb,则FIRST(b)除了ε加入集合FOLLOW(A)
  • 若B→aA,则FOLLOW(B)加入FOLLOW(A)
  • 若B→aAb,且ε∈FIRST(b),则FOLLOW(B)加入FOLLOW(A)
递归下降分析法

对于LL(1)文法,为每个非终结符构造子程序,子程序按照产生式的候选项分情况展开,遇到终结符则进行匹配,遇到非终结符则调用相应的子程序

  • 简单,易构造
  • 程序与文法相关,对文法的改变需要对程序进行修改
预测分析法

使用一张预测分析表,表的横坐标为非终结符,纵坐标为终结符,表的元素M[A,a]为关于A的产生式,表示当遇到符号a时用A推导应采用的产生式

构造表:

  • 对于每个文法产生式A→a|b,拆为两个部分A→a,A→b。分别观察
  • 将FIRST(a)的每个终结符a,加入A→a到M[A,a]。
  • 若FIRST(a)包含ε,则对FOLLOW(A)的每个终结符b,加入A→a到M[A,b]
  • 其他空格置为error

预测过程:

  1. 将#和文法开始符号s压入栈,栈顶符号记为TOP,输入符号记为X
  2. TOP=#,X=#,分析成功
  3. TOP=#,X≠#,分析错误
  4. TOP为终结符,TOP=X,则出栈,读入下一个符号
  5. TOP为终结符,TOP≠X,出错
  6. TOP为非终结符,M[TOP,X]=产生式A→a,则把a的符号从右到左入栈
  7. TOP为非终结符,M[TOP,X]=error,出错
3:自底向上的语法分析(移近-规约)

对于给定的字符串X,自左向右扫描,将输入符号逐个入栈,如果栈顶符号形成某个句型的可规约串,则用产生式的左部非终结符来代替,直到栈中只剩下开始符号则成功

LR分析法

根据当前分析栈中的符号串和向右顺序查看输入串的第N个符号,唯一缺点分析器的工作是移进还是规约,也就能唯一确定句柄

LR分析器的组成

  • 驱动器
  • 分析表
    • 动作表
    • 状态转换表
  • 分析栈
    • 符号栈
    • 状态栈

(3)语义分析

分析语法结构的含义,检查源程序是否包含静态语义错误,收集类型信息。

  • 进行类型分析和检查:检查数据类型的载体和运算是否正确
  • 内部表示源程序
    • 声明语句:使用符号表记录符号信息如符号,符号类型,符号的逻辑地址
    • 可执行语句:检查结构合理的表达式是否有意义
形式化方法
  • 属性文法
  • 公理语义
  • 操作语义
  • 指标语义

(4)中间代码生成

根据语义分析输出中间代码

语法制导翻译
  • 将语言结构的语义以属性的形式赋予该结构的文法符号
  • 将属性的计算以语义规则的形式赋予文法的产生式
  • 通过执行语义规则,实现对属性的计算,处理语义
中间代码的形式
  • 后缀式:将运算符写在运算对象后面(不需要使用括号,方便用栈求值)
  • 树形表示:运算符号为父结点,运算对象为两个子节点
  • 三元表示:(运算符,运算对象1,运算对象2)
  • 四元表示:(运算符,运算对象1,运算对象2,运算结果)
语法结构的翻译
使用到的语义变量:
语义变量 语义过程
Entry(id) 在符号表查找id并获取入口
S.code 翻译S形成的代码序列
E.place 存放非终结符E的变量名,在符号表的入口或整数码
E.tc E为真
E.fc E为假
GEN(OP,ARG1,ARG2,RESULT) 产生一个四元式
NXQ 下一个四元式地址的编号
Merg(P1,P2) 合并P1,P2两条链
Backpatch(p,t) 把p连接的所有四元式的第四项结果作为t(回填)
Newtemp 生成新的临时存储单元

拉链:四元式的转向地址不确定,将所有转向同一地址的四元式链接成表
回填:四元式的转向地址确定后,沿着链表填入地址

赋值语句
操作 文法 语义规则
保存值 A→id:=E {GEN(:=,E.place,-,Entry(id)) }
加法 E→E1+E2 {E.place:=Newtemp;GEN(+,E1.place,E2.place,E,place) }
乘法 E→E1*E2 {E.place:=Newtemp;GEN( * ,E1.place,E2.place,E,place) }
取反 E→-E1 {E.place:=Newtemp;GEN( @ ,E1.place,-,E,place) }
圆括号 E→(E1) {E.place:=E1.place }
赋值给变量 E→id {E.place:=Entry(id)}
判断语句
操作 文法 语义规则 说明
E→ E1 and E2 进行拆分
E → E1 and {Backpath(E1.tc,NXQ) ; E.fc=E1.fc} 如果E1为假,直接传假的出口
否则,判断E2,回传真的出口
E→E E2 {E.tc:=E2.tc ; E.fc=Merg(E.fc,E2.fc)} 如果E2也为真,传真的出口
合并假的出口
E→E1 or E2 进行拆分
E→E1 or {Backpath(E1.fc,NXQ) ; E.tc=E1.tc} 如果E1为真,直接传真的出口
否则,判断E2,回传假的出口
E→E E2 {E.fc:=E2.fc ; E.tc=Merg(E.tc,E2.tc)} 如果E2也为假,传假的出口
合并真的出口
E→not E1 {E.tc:=E1.fc ; E.fc=E1.tc} 交换真假出口
括号 E→(E1) {E.tc:=E1.tc ; E.fc=E1.fc} 传真的和假的出口
赋值 E→id {E.tc:=NXQ ; E.fc:=NXQ+1 ; GEN(jnz,Entry(id),-,0) ; GEN(j ,- ,-,0)} 等待回填
关系运算 E→id1 relop id2 {E.tc:=NXQ ; E.fc:=NXQ+1 ; GEN(j relop(运算符),Entry(id1),Entry(id2),0) ; GEN(j,-,-,0)} 等待回填
条件语句
操作 文法 语义规则 说明
赋值语句 S→A {S.chain=0} 无出口链
if语句 S→if E then S1 else S2 拆分
C→if E then S1 {Backpatch(E.tc,NXQ) ; C.chain:=E.fc} 如果为真,需要回填真出口;否则传假出口
S→CS1 S.chain:=Merg(C.chain,S1.chain) 拉链
Tq→CS2 else {q:NXQ ; GEN(J,-,-,0) ; Tq.chain:=Merg(S2.chain,q) ; Backpatch(C.chain,NXQ)} 如果为真需要回填真出口
S→TpS1 {S.chain:=Merg(Tp.chain,S1.chain);} 拉链
while语句 S→while E do S1 拆分
W→while {W:quad:=NXQ} 记录首地址
Wd→W E do {Backpatch(E.tc,NXQ) ; Wd.chain:=E.fc} 回填真出口,传假出口
S→WdS1 {Backpatch(S1.chain,Wd.quad); GEN(j,-,-,Wd.quad) ; S,chain = Wd.chain} 用首地址回填真出口,跳转出口
过程(函数)调用

依赖于形参和实参的结合方式和数据空间的分配方式

  • 静态分配:在编译时确定目标程序所需的全部数据空间的大小,并确定每个数据对象的存储位置(逻辑地址)
  • 动态分配
    • 栈分配(先申请后释放):将数据空间设计为栈,调用过程时入栈,过程结束后出栈
    • 堆分配:将数据空间设计为堆

活动:过程的一次调用称为活动

活动记录:活动的运行环境

  • 参数、返回地址
  • 机器状态
  • 局部数据、临时变量
  • 控制链、访问链

call P(T1,T2…) 产生四元式序列 para T1,para T2…call P,n

后端——与机器有关

(5)代码优化

在中间代码生成或目标代码生成时,分析程序的控制流和数据流,对程序进行等价变换,使得程序生成更有效的目标代码

  • 中间代码的形式
    • 解释器:适用于树型和后缀型
    • 编译器:适用于四元式
  • 目标代码的形式
    • 汇编语言:便于分析调试
    • 机器指令
      • 绝对机器指令:便于立即执行
      • 可再定位机器指令 :便于保存在任意位置
  • 寄存器的分配
  • 计算的次序

(6)目标代码生成

把中间代码变换为特定机器的绝对指令代码、可重定位的指令代码、汇编指令代码

符号表的管理

记录源程序各个符号的必要信息,可以在词语分析、语法分析、语义分析开始建立

出错处理

  • 静态错误:编译阶段出现的错误
    • 语法错误:拼写错误,符号错误
    • 静态语义错误 :语义分析阶段的类型不匹配错误
  • 动态错误:程序运行阶段的错误

3. 解释程序

不会产生目标程序,直接执行源程序或源程序的中间表示形式

(1)执行方式

直接执行源程序

对源程序进行逐个字符的检查,然后执行程序语句规定的动作

  • 效率低
执行中间表示形式

把源程序翻译某种中间代码的形式,再解释中间代码

  • 可移植

(2)解释程序的结构

分析部分——翻译为中间代码
  • 词法分析
  • 语法分析
  • 语义分析程序
解释部分——对第一部分进行解释
  • 程序计数器PC:记录执行的中间代码的位置
  • 内存数组M:存放中间代码和解释部分的子程序
  1. PC:=PC+1
  2. 执行位于opcode-table[M[PC]]的子程序

猜你喜欢

转载自blog.csdn.net/weixin_36904568/article/details/89716660