语言处理器
术语解释:
源语言:等待被转换的语言.
目标语言:转换后输出的语言.
编译器:一次将所有源语言转换成目标语言的软件系统.
解释器:在执行一句代码前才对该代码进行转换的软件系统.
编译器与解释器的区别:
编译器一次将所有源语言转换成目标语言,之后只用执行不用再次编译,但每次更改代码后都需要耗费大量时间编译.
解释器则一边转换一边执行,每次执行都需要进行转换,但省去了编译全部目标语言的时间.
即时编译器(JIT):
将源语言编译成中间语言,在执行时再将中间语言解释执行.
Java语言采用的就是JIT,程序编写完成后,先是通过javac将java文件编译成class文件,等需要执行时再通过java虚拟机(jvm)解释执行.
宏:
预处理器:负责将位置离散的源程序聚合在一起的程序,也负责宏展开.
链接器:解决外部内存地址问题.
加载器:把所有可执行目标文件放入内存中执行.
语言处理系统对源语言的处理过程:
源语言->预处理器->经过预处理的源语言->编译器->汇编语言->汇编器->可重定位机器代码->连接器与加载器->目标机器代码
编译器通常会生成汇编语言而不是机器码的原因:
相比机器码,汇编语言更容易输出和调试.
编译器结构
编译器由两大部分组成,分析部分与综合部分,分别对应编译器的前端与后端.
分析部分:
将源程序分解成多个组成要素,并在这些要素上加上语法结构.该结构用来创建该程序的一个中间表示,该表示可以用来分析语法构成是否正确和语义是否一致,并能提供给用户修正错误的提示信息.并将源程序相关信息存放在被称为符号表(symbol table)的数据结构中.最后将中间表示和符号表传递给综合部分.
该部分包括词法分析器,语法分析器,语义分析器与中间代码生成器
综合部分:
用分析部分提供的中间表示与符号表来生成用户期待的目标程序.
该部分包括机器无关代码优化器,代码生成器与机器相关代码优化器.
术语解释:
符号表:存放程序符号的一张表.
词法分析:是将读入的源程序字符串流组织成有意义的词素序列的词法分析器.
词素:语法功能最小单位.
词法单元:每一个词素都会有一个词法单元(token).
形如
token-name是语法分析步骤使用的抽象符号同时也是分析部分输出的中间表示.
attribute-value是符号表中关于这个词法单元的条目.
语法分析:是使用由词法分析器生成的各个词法单元的第一个分量来创建树形中间表示(即语法树)的语法分析器.
语义分析:是使用语法树和符号表中的信息来检查源程序是否和语言定义的语义一致的语义分析器.
中间代码生成:这里生成的代码是中间表示的一种,与语法树不同,这里生成的中间表示是低级语言或类机器语言.该中间代码由编译器构造,可构造多个.该中间表示有两个重要的性质:易于生成和能够被轻易的转换成目标机器上的语言.该中间表示是介于源语言与目标语言之间的过渡语言.
三地址指令:它是中间代码生成的一种中间表示.
该指令有三个特点:
每个三地址赋值指令最多只有一个运算符.
编译器应该生成一个临时名字存放一个三地址指令计算得到的值.
部分三地址指令运算分量少于三个(id1=t3,该三地址指令只有2个分量).
代码优化:机器无关代码优化用于改进中间代码,使其在某一方面更加优越(更快,更短等).
代码生成:将输入的中间代码转换成目标代码的过程.涉及寄存器分配与内存地址选择.
符号表管理:符号表是一种数据结构,用于记录变量的名字,类型等信息.
将多个步骤组合成趟:上述的多个过程可以组合成一趟,针对源代码的是前端趟,针对目标机上的是后端趟,优化是可选趟.一个前端趟可以与不同的后端趟组合使用.
构建一个编译器的相关科学
编译器设计和实现中的建模要求:通用性和功能的要求与简单性和有效性之间的平衡.
代码优化的科学:使用严格的数学证明,证明一个优化是正确的,并且对它所有的输入都产生预期的结果.
编译器优化的设计目标:
优化不能改变被编译程序的含义.
优化必须改善很多程序的性能.
优化所需的时间必须保持在合理的范围内.
所需要的工程方面的工作必须是可管理的.
编译器技术的应用:
弥补高级语言因为引入高层次抽象导致的低效率.
过程内联技术:把一个过程(java中的方法)替换为相应的过程体.目的是把多个过程融合成一个,以优化代码.
编译技术的应用
高级语言的实现:高级语言对底层硬件的控制力很差,而编译器则提供了优化其生成代码性能的技术.
针对计算机体系结构的优化
并行性:编译器可以安排指令,以使指令级并行更加有效.
内存层次结构:编译器可使内存层次结构更加高效.
程序设计语言基础
静态语言和动态语言的区别:静态语言在编译时确定其变量作用域,动态语言在运行时确定其变量作用域.
其它例子:java的static关键字则是确保使用该关键字的变量有一个固定的内存位置,而不是如同上面所说的用来确保作用域.
抽象来看,静态语言和动态语言,前者在编译之后就不会改变,而后者会改变.
环境与状态
环境:从名字到内存位置的映射.
状态:内存位置到它们的值的映射.
名字,标识符,变量
名字:术语名字和变量通常指的是同一个事物,我们还是要很小心的使用它们,以便区别编译时刻的名字和名字在运行时刻所指的内存位置.
标识符:它用来指向一个实体,比如一个数据对象,过程,类或类型.所有标识符都是名字,但不是所有名字都是标识符.名字也可以是一个表达式(表达式不指向实体).
变量:它指向存储中的某个特定位置.
名字与变量的区别:
名字:名字可指向多个变量,比如定义在全局作用域的i与局部作用域的i,他们的名字都是’i’,但他们的内存地址不一样.
变量:一个变量仅指向一个内存地址.
过程,函数,方法
过程:可以被调用的子程序,.如同c一样的语言只有函数,因此c语言把过程当作是具有特殊返回类型’void’的函数来处理.
函数:函数返回某个类型的值,过程则不返回.
方法:c++和java这样面向对象的语言使用术语’方法’,方法可以想函数或过程一样运行,但方法总和某个类有关联.
过程,函数,方法的相同点:都是一段可执行的代码.
过程,函数,方法的区别:
过程:不返回值.
函数:返回值.
方法:与某个类有关联.
声明和定义
声明:声明告诉我们事物的类型.
定义:定义告诉我们它的值.
静态作用域
一个声明的作用域由该声明在程序中出现的位置隐含的决定.
动态作用域
一个声明的作用域依赖于所有当前调用的函数,然后选择其中最近调用的且有该声明的函数.
静态作用域与动态作用域的类比
静态作用域寻找的声明位于最内层的,包含变量使用位置的单元中.动态作用域寻找的声明位于最内层的,包含变量使用时间的单元中.
参数传递机制
值调用:在该调用中,会对实参求知(当它是表达式时)或拷贝(当它是变量时),这些值被放在属于被调用过程的相应形式参数的内存位置上.诸如c和java等语言,如果实参是数组,那么实际上拷贝的是该数组的指针或引用,这导致修改其拷贝就是修改其本身,因此被调用过程是可以改变对象本身.
引用调用:实参的地址作为相应形式参数的值被传递给被调用者.如果实参是表达式,那么就会对表达式求值并放入形参中,修改该变量并不会修改其被拷贝变量本身.
别名:同一个变量的不同名字.