编译原理笔记
文章目录
- 编译原理笔记
- day1
- 什么是编译?
- 编译器的结构
- 词法分析概述
- 词法分析的主要任务
- 语法分析概述
- 主要目的
- 主要任务
- 具体实例
- 语义分析概述
- 主要目的
- 主要任务
- 中间代码生成和编译器后端
- 常用的中间表示形式
- 目标代码生成器
- 代码优化器
day1
什么是编译?
机器语言:机器语言,又称机器码或二进制指令,是计算机能够直接执行的最底层的语言。它由“0”和“1”组成的二进制代码组成,代表了特定的操作指令和操作数。计算机利用硬件电路执行机器语言指令,它是一种非常原始和低级别的语言,通常很难被人类理解和编写。汇编语言:汇编语言是机器语言的一种相对易于理解和编写的形式,它通过使用助记符(mnemonics)来代替二进制码,使得程序更易于编写和理解。
汇编语言把程序员编写的指令翻译成具有相同功能的机器语言指令,并且提供了方便的符号和标签来引用内存地址和操作数。在汇编语言中,程序员需要手动处理计算机内存的分配和管理,这使得汇编语言在编写操作系统、设备驱动程序和嵌入式系统等方面非常有用。
高级语言:高级语言是相对于机器语言和汇编语言而言的,它是一种更为人性化和易于理解的程序设计语言。高级语言使用英语单词、符号和表达式等符号来表示计算机指令和操作,它提供了更高层次的抽象,隐藏了许多底层的细节,并且通常比机器语言和汇编语言更易于学习和编写。一些常见的高级语言包括C、C++、Python、Java、JavaScript等。在编写高级语言程序时,程序员无需关注计算机的底层细节,程序的执行由编译器或解释器负责将高级语言翻译成底层的机器语言或汇编语言,然后交给计算机执行。
预处理器:预处理器(Preprocessor)是一个独立于编译器的程序,它在编译源代码之前对代码进行处理。预处理器能够识别并处理以#开头的特殊指令,例如#include和#define,它们通常用于引入头文件、宏定义和条件编译等。预处理器的主要作用是对源代码进行预处理,生成一个被修改过的文本文件,这个文件将成为实际编译的源代码。
编译器:编译器(Compiler)是将高级语言源代码转换为目标机器的可执行代码的程序。编译器将源代码作为输入,并将其翻译成机器语言或汇编语言代码。编译器执行的过程包括词法分析、语法分析、中间代码生成、代码优化和目标代码生成等。编译器的主要功能是将高级语言源代码翻译成机器代码,使得计算机能够执行程序。
汇编器:汇编器(Assembler)是将汇编语言代码转换为机器语言代码的程序。它将汇编代码作为输入并将其翻译成二进制的机器码,这些机器码可以直接被计算机执行。汇编器的主要作用是将程序员编写的汇编语言代码翻译成机器语言代码,以便计算机可以理解和执行。
链接器:链接器(Linker)是一个程序,它将多个目标文件和库文件链接在一起,生成一个单独的可执行文件。目标文件是编译器生成的可重定位的机器代码文件,包含未解决的符号和地址信息。库文件是包含一组函数和变量定义的对象文件,它们被编译为可重定位的机器代码,并被组合成一个或多个库。链接器的主要作用是解决符号引用、地址重定位和代码合并等问题,生成一个完整的可执行程序。
编译器的结构
编译器通常由以下几个模块组成:
- 词法分析器(Lexer):将源代码输入到编译器中,词法分析器会对源代码进行扫描并将代码分解成多个词法单元(token),例如关键字、运算符、标识符等等。
- 语法分析器(Parser):对词法单元进行语法分析,检查代码是否符合语法规则。通常采用上下文无关文法(CFG)来描述语法规则,将词法单元转化为语法树。
- 语义分析器(Semantic Analyzer):在语法树的基础上进一步进行语义分析,如类型检查、变量声明等。如果存在语义错误,会报告错误并停止编译。
- 代码生成器(Code Generator):将语法树转化为目标代码,这个过程通常包括代码优化,包括去除无用代码、减少指令数等等。
- 目标代码生成器(Target Code Generator):将目标代码转化为机器码,完成编译过程。以上就是编译器大体的结构。当然,不同编译器在实现细节和组件划分上会存在差异。
词法分析概述
编译的第一个阶段,相当于你翻译一段英文语句你肯定是先看其词性
词法分析的主要任务
词法分析的主要任务是将源代码分解成词法单元(token),也称为词法分析单元(lexical unit),并将其传递给语法分析器。其主要任务包括:
1. 分离词法单元:对输入的源代码文件进行扫描,将代码分解成多个词法单元,如关键字、运算符、标识符、常数、字符串等等。
**2. 处理注释:**将注释从源代码中忽略掉,使其不被语法分析器处理。
**3. 处理空白符:**将空格、制表符和换行符等空白符忽略,不作为词法单元处理。
**4. 进行词法错误检查:**对无法识别的符号、不合法的标识符等进行错误检查,报告错误。
**5. 输出词法单元:**将词法单元以某种格式输出,方便后续的语法分析器处理。总的来说,词法分析器的主要任务是将源代码转化为为一系列词法单元,并将其传递给语法分析器进行后续处理。
相关例子
语法分析概述
主要目的
在语法分析的过程中,编译器会将词法分析器生成的词法单元流转化为抽象语法树(AST)。抽象语法树是一种树状数据结构,可以表示编程语言中的控制流、数据类型、操作符以及变量等等,是后续处理的基础。
主要任务
- 检查语法错误:对源代码进行扫描,检查代码是否符合语法规则,如果出现语法错误,将错误信息报告给程序员。
- 生成抽象语法树:将符合规则的代码转化为抽象语法树,方便后续的语义分析和代码生成处理。
- 语法分析器的设计:选择合适的语法分析算法,如递归下降分析、自底向上分析等,以及如何构建语法树等值得研究。
具体实例
语义分析概述
主要目的
语义分析是编译器中的一个重要组成部分,它的任务是在语法分析的基础上进一步检查程序的语义正确性。语义分析器会对抽象语法树(AST)进行处理,进行类型检查、变量声明、作用域检查等。
主要任务
语义分析的主要任务包括:
类型检查:检查变量、表达式、函数等的类型是否匹配,比如进行浮点数和整数类型之间的转换,或者检查类型不匹配的表达式计算。
变量声明:检查变量是否被正确声明,是否存在重复声明等问题。
作用域检查:检查变量、函数等的作用域是否正确,包括变量的生命周期、作用域的嵌套等。
常量折叠:常量表达式可以在编译时计算,将常量表达式折叠为常量值,提高代码的执行效率。
其他语义检查:如检查数组下标是否越界、函数调用参数数量是否匹配等。
总的来说,语义分析是编译器中的一个重要环节,其主要目的是确保程序的语义正确性,发现并报告语义上的错误,并对程序进行必要的优化。语义分析通常在语法分析后进行,将抽象语法树(AST)作为输入,并生成带有语义意义的中间代码。
中间代码生成和编译器后端
常用的中间表示形式
三地址码|语法结构树/语法树
三地址码:
三地址码(three-address code)是一种中间代码表示形式,用于代码优化和生成的过程,在编译器、解释器等程序中广泛使用。它是一种将高级语言转化为低级机器语言的过程中间结果。
三地址码表示一个表达式时,将其分解为多个简单操作,每个简单操作只包含一个运算符和一些操作数。每个操作数都是一个临时变量或常量,被分配一个唯一的标识符。
三地址码通常由三个部分组成,分别是操作符、源操作数1和源操作数2以及目标操作数,它们之间通过“=”符号分隔开来。例如:
t1 = a + bt2 = 2 * t1c = t2 + d
上面的代码包含三个简单操作,每个操作都有一个运算符和一些操作数。t1、t2、c 和 d 是临时变量或常量,它们在操作中被用作操作数和目标操作数。通过将高级语言转化为三地址码,可以方便进行代码的优化和生成。三地址码具有简洁、规范、易于生成和优化的优点,但其缺点是生成的代码比源代码更加冗长,会占用更多的存储空间。
目标代码生成器
目标代码生成器是编译器的一个组成部分,主要用于将中间代码转化为目标机器代码,以便在目标计算机上运行程序。目标机器代码通常是特定机器或操作系统架构的二进制代码,如x86、ARM等。
目标代码生成器的主要任务是将中间代码串转化为目标机器码指令串。这个过程包含以下几个主要步骤:
- 寄存器分配:将每个变量映射到目标机器的物理寄存器,以便在运行时快速访问变量的值。
- 指令选择:根据目标机器的指令集,将中间代码转化为目标机器的指令,同时进行一些指令的优化,例如使用寄存器移位操作代替乘法操作。
- 块划分:将中间代码串分成基本块,每个块包含一组顺序执行的指令,可以轻松地转化为目标代码的基本块。
- 代码重排:通过修改指令顺序来减少条件跳转的使用和增加指令的并行度,从而提高程序的执行效率。
- 代码生成:将指令块转化为目标指令序列,并生成目标二进制代码。
代码优化器
代码优化器是编译器的一个重要组成部分,主要用于对生成的中间代码进行优化,以提高程序的执行效率和减少运行时间、内存占用等资源消耗。
代码优化器的主要任务是在不改变程序语义的情况下,对中间代码进行优化。它可以对代码进行静态分析,找出其中的潜在问题和瓶颈,并采取一系列优化手段来改善代码性能。
优化的方式多种多样,包括但不限于以下几种:
常量折叠:将常量表达式在编译期间计算,提高代码执行效率。
减少函数调用次数:将同一函数的多次调用合并为一次,减少函数调用开销。
死代码消除:将代码中不会被执行的语句删除,减少程序运行时间和内存占用。
循环展开:将循环的迭代次数展开,减少循环次数,提高代码执行效率。
寄存器分配:为变量选择最优的寄存器,减少内存访问次数,提高代码执行效率。
指令选择:选择最优的机器指令,减少指令执行时间,提高代码执行效率。
数据流分析:在程序运行时收集程序数据,并进行优化。
代码优化器的优化效果直接影响程序的性能和运行效率。但是,进行代码优化的代价也很高,过度的优化可能会导致代码变得复杂难以维护,甚至会引入新的错误。因此,编译器开发人员需要权衡优化和代码的可读性、可维护性,以及对编译器开发成本的影响等因素。