编译器原理

一、编译原理

1. 编译的概念

编译器是将源代码转化成机器码的软件;所以编译的过程则是将源代码转化成机器码的过程,也就是 cpu 可执行的二进制代码。

编译的过程大概可以分为三个阶段,这也正是当前主流的编译器架构,即:编译前端(frontEnd)、中间代码优化(optimizer)、编译后端(backEnd)。

编译前端将源代码转化成中间代码。其详细过程包括:预处理、词法分析、语法分析、生成中间代码;

中间代码优化则是对编译器生成的中间代码进行一些优化,最终提供给编译后端;

编译后端根据不同的 cpu 架构,将中间代码汇编,产生汇编代码,最后解析汇编指令,生成目标代码,也就是机器码;

至此,编译器的工作结束。但是,机器码可以被 cpu 识别,却不能直接执行的。要生成可执行文件,还需要进行链接操作。

链接主要是指将各个文件、静态库、动态库进行导入整合。其中最重要的部分就是链接器依靠重定位表对需要重定位的地址进行重新赋值;因为在链接之前,.o 文件不知道自己 import 的文件的位置,也就不知道自己所调用的函数的地址,所以在生成 .o 文件时,这些函数地址会使用 0 代替,并使用重定位表进行记录。在链接时,依靠重定位表,将具体的函数地址,对每个需要重定位的地址进行重新赋值。

总结

对于 C 、C++ 、OC 这几门语言来讲,从源码到运行需要经历编译前端处理、中间代码优化、编译后端汇编和链接三个步骤。编译器接收源代码,输出由机器码组成的目标文件(二进制格式,.o 后缀),最后链接器将各个目标文件链接起来,执行重定位,最终生成可执行文件。

二、编译器的架构

传统编译器比如 gcc 会将编译的整个过程揉在一块,但是 LLVM 采用全新的架构,分为 frontEnd 、optimizer、backend三层架构:


编译器原理_第1张图片
LLVM架构

三、编译 VS 解释

编译型语言和解释型语言可以从三个角度进行区分:

1. 如何区分解释和编译语言

C 语言等编译型语言会最终生成包含机器码的可执行文件,程序运行时,所有代码统一执行。

javaScript 、python 等解释型语言会逐条翻译,逐条执行。

java 生成 .class 的字节码,类似于 C 语言中的中间代码。然后, .class 字节码会被加载进入 java 的虚拟机,逐条进行解释执行。如果单从这个角度就对语言进行区分,那么 java 是不是也可以称之为解释型语言呢?哈哈哈~~~

另外,因为 x86 架构比较复杂,即使是机器码,cpu 也会将其转化成更底层的代码。如此而言,硬件就相当于一个虚拟机的作用,那么 C 语言也有可能是运行时进行解释执行的,只不过是 cpu 在进行解释,那么如此而言, C 语言也可能是解释型语言??

其实,关键有两点:ATS(抽象语法树)是否已经生成、是否还需要转化成中间代码。

但是更关键的是。。。

2. 看清楚本质

其实区分解释型语言和编译型语言是后话。语言知识一种工具,适用场景决定了使用哪种工具。你难道要带个厨子去炒菜,用牛刀来杀鸡?

C 语言的优点是生成机器码,直接和硬件交互,速度基本可以达到极限值。但是其缺点也很明显,需要安装各种依赖库、导入各种系统库。

javaScript 速度慢,但是其有点也很明显。找个 ide 就能写,甚至是一个 text 都能写。写完放到不同的环境中,比如 node、比如浏览器,一份代码跨平台执行。

所以,C 语言的适用场景是那些对速度要求很高的场景,javaScript、python更多的使用在一些小工具、小脚本、Web开发等等。当然,javascript 的性能已经得到了较大的提升,但是即使如此,做大型游戏时,涉及到的计算量简直不要不要,这个时候你选择用 C++ 开发还是 javascript?

所以,使用需求决定使用场景,最终决定语言的选择。语言被选择之后又会反哺需求,会有和需求匹配的更多的系统库、三方库、三方代码等的出现。再之后,才会讨论变易语言和解释语言的区别,总感觉有点马后炮的意思。

你可能感兴趣的:(编译器原理)