编译原理
编译,**其实就是把源代码变成目标代码的过程。**如果源代码编译后要在操作系统上运行,那目标代码就是汇编代码,我们再通过汇编和链接的过程形成可执行文件,然后通过加载器加载到操作系统里执行。如果编译后是在解释器里执行,那目标代码就可以不是汇编代码,而是一种解释器可以理解的中间形式的代码即可。
编译器翻译源代码,也需要经过多个处理步骤
词法分析(Lexical Analysis)
在编译之前,源代码只是一长串字符而已,这显然不利于编译器理解程序的含义。所以,编译的第一步,就是要像读文章一样,先把里面的单词和标点符号识别出来。程序里面的单词叫做 Token,它可以分成关键字、标识符、字面量、操作符号等多个种类。把字符串转换为 Token 的这个过程,就叫做词法分析。
语法分析(Syntactic Analysis)
让编译器像理解自然语言一样,理解它的语法结构。这就是第二步,语法分析。
语法分析阶段也会把 Token 串,转换成一个体现语法规则的、树状的数据结构,这个数据结构叫做抽象语法树(AST,Abstract Syntax Tree)
这样的一棵 AST 反映了示例程序的语法结构。比如说,一个函数的定义包括了返回值类型、函数名称、0 到多个参数和函数体等。这棵抽象语法树的顶部就是一个函数节点,它包含了四个子节点,刚好反映了函数的语法。
语义分析(Semantic Analysis)
- add 节点:把两个子节点的值相加,作为自己的值;
- 变量节点(在等号右边的话):取出变量的值;
- 数字字面量节点:返回这个字面量代表的值。
语义分析是要解决什么问题呢?
比如
int a = 10; //全局变量
int foo(int a){ //参数里有另一个变量a
int b = a + 3; //这里的a指的是哪一个?
return b;
}
而把“a+3”中的 a,跟正确的变量定义关联的过程,就叫做引用消解(Resolve)。这个时候,变量 a 的语义才算是清晰了。
语义分析的重要特点,就是做上下文相关的分析。
带有标注信息的 AST
在语义分析阶段,编译器会做语义理解和语义检查这两方面的工作。词法分析、语法分析和语义分析,统称编译器的前端,它完成的是对源代码的理解工作。
做完语义分析以后,编译器需要懂得目标语言,也就是懂得目标语言的词法、语法和语义,这样才能保证翻译的准确性。生成目标代码(后端代码)的工作,叫做后端工作。
汇编器能够将汇编代码转换成机器码。
优化(Optimization)
为啥要做优化
- 源语言和目标语言有差异:源语言的设计目的是方便人类表达和理解,而目标语言是为了让机器理解。在源语言里很复杂的一件事情,到了目标语言里,有可能很简单地就表达出来了。
- 是程序员写的代码不是最优的,而编译器会帮你做纠正
生成目标代码
- 第一,是要选择合适的指令,生成性能最高的代码。
- 第二,是要优化寄存器的分配,让频繁访问的变量(比如循环变量)放到寄存器里,因为访问寄存器要比访问内存快 100 倍左右。
- 第三,是在不改变运行结果的情况下,对指令做重新排序,从而充分运用 CPU 内部的多个功能部件的并行计算能力。
计算机语言编译和自然语言的翻译异同
做的事情本质上都是输入“遵循某种文法所说出的话”,输出“按照另外一种文法,把同样的话给表达出来”,这么件事~ 如果说区别的话,处理的文法是不一样复杂的,自然语言是0型文法,而计算机语言的处理基本上都是在2型文法和3型文法的层面上,只有涉及意义处理才会做一些1型文法层面的事儿。 毕竟,计算机语言在设计时就要考虑到可以被计算机去执行,所以其必须考虑逻辑严谨; 而自然语言,中文、英文这些语言的历史包袱很重,而且语言最初设计的目的也不是为了给机器去跑,而是服务于广大人民群众呀~ 按形式语言这门学科的定义,自然语言对应的是图灵机,所以对自然语言的处理已经是AI领域研究的范畴~(https://cloud.tencent.com/developer/article/2352528)
- 点赞
- 收藏
- 关注作者
评论(0)