编译与解释
定义:从人们容易理解的形式转化为计算机能执行的形式(机器指令)。翻译器分为:解释器与编译器。
1、解释器:将源代码转化成一些动作并立即执行。不产生目标程序,根据当前的机器处理器芯片,边解释边执行。优点:速度快。从写代码到执行代码几乎能立即完成。但是依旧有局限性。解释器必须贮存在内存中以执行程序,如此空内存空间限制。
1)解释程序适合程序员交互方式的工作情况,即希望在获取下一个语句之前了解每个语句的执行结果,允许执行时修改程序。解释程序的输入包括源程序和源程序的输入数据。在解释程序工作的整个过程中,源程序、符号表等内容始终放在存储区,并且存放格式要设计得易于使用和修改。--->解释程序的存储区内容:解释系统、源程序、工作单元及名字表、符号表、缓冲区、栈区。
2、编译器:将源代码转化为汇编语言或者是机器指令。编译过程完成从源程序到目标程序的翻译工作,是一个复杂的整体过程。编译过程分为:源程序->词法分析->语法分析->语义分析->中间代码分析->代码优化->目标代码生成。
1)词法分析:从左到右一个字符一个字符的读入源程序,对构成源程序的字符进行核扫描和分解,从而识别出一个个单词。
2)语法分析:在词法分析的基础上将单词序列分解成各类语法短语,这样的语法短语可以表示成语法树。
3)语义分析:审查源程序有无语义错误,为代码生成阶段收集类型信息,比如会有类型审查。查看每个算符是否具有语言规范允许的运算对象。检查参数是否正确使用,为静态类型检查。
4)中间代码生成:有的编译程序将源程序编程一种内部表示形式,这种内部表示形式叫做中间语言或中间代码。中间代码是一种结构简单、含义明确的记号系统。
5)代码优化:使生成的目标代码 更为高效、省时间和空间。
6)目标代码生成:将中间代码变换成特定机器上的绝对指令代码或可重定位的指令代码或汇编指令代码。涉及到硬件系统功能不见的运用、机器指令的选择、各种数据类型变量的存储空间分配以及寄存器和后缓寄存器的调度
一个完整的编译程序还包括表格管理程序和出错处理程序。编译过程中源程序的各种信息被保留在不同的表格里。于是一个表格管理就势必提上日程。
在源程序被编译阶段,存储区为源程序和目标代码开辟空间,存放编译的表格,存取区分为:源程序缓冲区、名字表、目标代码缓冲区、编译程序中用中间表示及各种表格。在运行时存储去中包括目标代码区、数据区。有时在语法分析后使用全局优化器来生成更段、更快的代码。编译后的程序较小。使用连接器将各段程序连接成一个完整的可执行程序就是我们下面将的分段编译。
分段编译:每次创建和测试 的一部分,当这部分程序能正常运行后,将它作为程序组块保存起来将测试通过并能正常运行的程序收集起来加入库中。一些编译器采用了内存中编译。大多数编译器,编译时每一步都要读写文件。内存中编译器将编译器程序放在RAM中,对于小程序来说,内存中编译器几乎能和解释器一样相应。
3、链接器:将目标模块连接成为一个可执行程序操作系统可以装载和运行它。当某个目标模块中的函数引用另一个目标模块中的函数或变量时,由连接器来处理这些引用。将编早起的连接器对目标文件和库文件只查找一次。因此目标文件和库文件的顺序就非常重要。
1)当C++或C对函数和变量进行外部饮用时,若还未遇到这个函数或变量的定义,连接器会把它的标识符加到未解析引用列表中。在连接的目标模块中未找到函数或变量的定义。它将去库中查找,通过索引,将找到的目标模块加载进入可执行程序。连接器按照指定的顺序查找文件,当用户的函数与库函数同名之后,我们创建的函数将会在库函数的前面,于是会引用这个自己定义的函数,这个时候就要使用命名空间来解决问题。当创建一个C++/C可执行程序是,连接器会秘密连接某些模块。其中之一是启动模块。它包含了对程序的初始化历程。初始化历程是开始执行程序时必须首先执行的一段程序。初始化例程建立堆栈,并初始化程序中的某些变量。
2)其实操作系统实际并不调用我们所写的入口函数,它会调用由运行库实现并在连接时使用-entry:命令行选项来设置一个C/C++运行时启动函数,该函数将初始化运行库。使我们能够调用malloc和free之类的函数。它还确保我们的代码开始执行之前,声明的任何全局和静态C++对象都被正确的构造。
3)启动函数的用途为:
获取指向新进程的完整命令行的一个和指针
获取指向新进程的环境变量的一个指针
初始化C/C++运行库的全局变量。
初始化C运行库内存分配函数和其它底层I/O例程使用的堆
调用所有全局和静态C++类对象的构造函数
完成这些初始化工作后。C?C++启动函数就会调用应用程序的入口点函数。
而Java提供的编译器是把源文件编译成字节码的一种中间代码。当字节码加载到内存后,再由Java的解释器对字节码进行解释,Java的字节码很容易使用JIT编译技术将字节码直接转化为高性能的本地机器码。