龙年读龙书——如何学习编译原理(一)

我想成为二进制高手!

虽然不确定这个学了对二进制有没有用,但学校也有这个课,就开始看了。

第一章

编译器(complier):将源语言翻译成目标语言,并可以报告错误的一个程序。

从源程序形成可执行文件,需要经过预处理器(preprocessor),再通过编译器产生汇编语言程序,由汇编器(assmbler)处理,生成可重定位的机器代码,连接器(linker)解决内存地址问题,再由加载器(loader)统一执行。

编译器结构和步骤:

两个部分:分析(analysis)和综合(synthesis),也分别称为编译器的前端(front end)和后端(back end)。

步骤:

  • 词法分析(lexical analysis)或扫描(scanning),形成有意义词素(lexeme)。词法分析产生词法单元(token)作为输出:<token-name,attribute-value>。类似于一个 <属性 ,存储位置> (?瞎理解的)的东西。
  • 语法分析(syntax analysis)或解析(parsing),常用语法树(syntax tree)。类似于表达式求值这种,当然肯定复杂很多。
  • 语义分析(semantic analysis),检查源程序是否符合源语言的规定,一个重要部分是类型检查(type checking),支持一些自动类型转换(coercion)。
  • 中间代码生成,三地址代码(three-address code),用类似于汇编的指令组成,形如 or 是某种运算。
  • 代码优化和代码生成。
  • 符号表管理。符号表记录源程序中的各种变量名和属性相关信息,支持编译器查询/修改/存储每个名字的记录。
  • 最后统一为一个趟(pass)。

然后是一些杂项,包括历史,应用,体系架构啥的。

程序语言设计基础

静态(static)和动态(dynamic):取决于一个问题是否可以在编译时刻(complie time)决定,还是在运行时刻(run time)决定。

作用域(scope):一个 变量 的声明 的作用域 指程序的一个区域 使得 这个区域的对 的使用 都指向这个声明。如果可以仅通过阅读程序可以确定作用域,则称这个语言使用静态作用域(static scope),或词法作用域(lexical scope),否则为动态作用域(dynamic scope)。

环境(environment)与状态(state):环境是一个从名字到存储位置(变量,C 语言中的 “左值”)的映射,状态是一个位置到值的映射(C 语言中,即把左值映射为右值)。

参数传递机制:

实在参数:调用过程中使用的参数。

形式参数:过程定义中使用的参数。

  • 值调用(call-by-value):对参数求值或拷贝变量。需要注意的是,很多指针是值调用,也可以更改变量的值,比如传递数组名。
  • 引用调用(call-by-reference):传递实在参数的地址。类似于传指针,会修改参数的值。但是如果是表达式,调用前会先求值并存储到一个新位置上,此时修改不会影响数据。
  • 名调用,今天不再采取,忽略(

别名(alias):就是两个形式参数指向同一个位置,修改一样的东西。

第二章

上下文无关文法(context-free grammar):

  • 终结符号(terminal),就是前面的词法单元。
  • 非终结符号(nonterminal),“语法变量“,表示一个终结符号串的集合。
  • 产生式(production),包括一个产生式头部或左部的非终结符号,箭头,一个产生式体或右部的由上面两个符号组成的序列,表示产生式头代表的一种构造的书写形式。形如 ,其中 是一个非终结符号, 是串,表示 可以由 中的某种方式构造。
  • 开始符号(starts symbol),指定的一个非终结符号。

推导:从开始符号串出发,不断将某个非终结符号替换为其某个产生式的体。

语言(language):一个文法可以推导出的所有终结符号串的集合。

语法分析(parsing):接受一个终结符号串,找出推导方法。如果不能,报告语法错误。

语法分析树(parse tree):将推导过程表示成树形结构。构造方式为,如果使用产生式 ,则将 按序作为 的子节点接上去。

二义性(ambiguous):一个文法有多个语法分析树可以生成同一个终结符号串。

运算符的结合(associate):左结合是运算分量两侧都有运算符时,属于左边的运算符,右结合则属于右边。简单理解就是 等价于 还是 是某个运算。

运算符的优先级:很熟悉的东西,不多说了(

后面一些内容在之后的章节会有具体讲述,所以 gugugu