⑴ 请问编写软件的第一个语言程序是如何诞生的
早期的计算机软件都是用汇编语言直接编写的,这种状况持续了数年。当人们发现为不同类型的CPU编写可重用软件的开销要明显高于编写编译器时,人们发明了高级编程语言。由于早期的计算机的内存很少,当大家实现编译器时,遇到了许多技术难题。
大约在20世纪50年代末期,与机器无关的编程语言被首次提出。随后,人们开发了几种实验性质的编译器。第一个编译器是由Grace Hopper于1952年为A-0语言编写的。通常认为,1957年由IBM的约翰·巴科斯领导的FORTRAN团队介绍了第一个完整的编译器。1960年,COBOL成为一种较早的能在多种架构下被编译的语言。
高级语言在许多领域流行起来。由于新的编程语言支持的功能越来越多,计算机的架构越来越复杂,这使得编译器也越来越复杂。
早期的编译器是用汇编语言编写的。首个能编译自己源程序的编译器是在1962年由麻省理工学院的Hart和Levin制作的。从20世纪70年代起,实现能编译自己源程序的编译器变得越来越可行,不过还是用Pascal和C语言来实现编译器更加流行。制作某种语言的第一个能编译器,要么需要用其它语言来编写,要么就像Hart和Levin制作Lisp编译器那样,用解释器来运行编译器。
引自 : 维基网络
⑵ 编译器与解释器的区别和工作原理
编译器与解释器的区别和工作原理
编译器和解释器是计算机程序语言处理中的两种基本工具,它们的主要区别在于处理源代码的方式和执行效率。
一、区别
工作方式:
编译器:将源代码一次性转换成目标代码(机器语言),然后生成可执行程序。这个过程在程序执行之前完成,用户拿到的是可以直接运行的程序。
解释器:在程序运行时,逐行读取源代码,将其转换成中间代码(如字节码),然后解释执行。解释器不会生成独立的可执行程序,而是作为源代码和机器之间的桥梁。
执行效率:
编译器:由于编译后的程序是机器语言,可以直接被CPU执行,因此执行速度通常较快。
解释器:每次执行都需要将源代码转换为中间代码,再由解释器解释执行,因此执行速度相对较慢。但解释器可以动态地解释执行代码,具有更高的灵活性。
跨平台性:
编译器:生成的可执行程序与特定的CPU架构和操作系统相关,因此跨平台性较差。需要在不同的平台上重新编译源代码。
解释器:只要平台上有相应的解释器,源代码就可以在该平台上运行,无需重新编译。因此,解释型语言通常具有更好的跨平台性。
二、工作原理
编译器的工作原理:
预处理:处理源代码中的宏定义、包含文件等,生成预处理后的源代码。
编译:将预处理后的源代码转换为目标代码(二进制机器语言)。
链接:将目标代码与库文件(如操作系统提供的API)链接,生成可执行程序。
编译器的工作流程可以概括为:源代码 → 预处理器 → 编译器 → 目标代码 → 链接器 → 可执行程序。
解释器的工作原理:
词法分析:将源代码逐行读取,分解成单词(token)。
语法分析:根据语法规则,将单词组成语法树(parse tree)。
中间代码生成:将语法树转换为中间代码(如字节码)。
解释执行:解释器逐条读取中间代码,将其转换为机器指令并执行。
解释器的工作流程可以概括为:源代码 → 解释器 → 词法分析 → 语法分析 → 中间代码生成 → 解释执行。
三、示例说明
以Python为例,它是一种解释型语言,使用解释器执行源代码。Python的源代码无需预先编译成可执行程序,而是在程序执行时,由解释器逐行读取并解释执行。Python的解释器有多种,如CPython、IPython、PyPy等,它们各自具有不同的特点和性能表现。
四、JIT即时编译器
JIT(Just-In-Time)即时编译器是一种结合了编译器和解释器优点的技术。它在程序运行时,将频繁执行的代码(热点代码)编译成机器指令,以提高执行效率。JIT编译器可以在解释器的基础上,动态地优化代码的执行速度。Java虚拟机(JVM)中的JIT编译器就是一个典型的例子。
综上所述,编译器和解释器在工作方式、执行效率和跨平台性等方面存在显着差异。了解它们的区别和工作原理,有助于我们更好地选择和使用编程语言及其工具链。
⑶ 什么是编译器
编译器
编译器是一种特殊的程序,它可以把以特定编程语言写成的程序变为机器可以运行的机器码。我们把一个程序写好,这时我们利用的环境是文本编辑器。这时我程序把程序称为源程序。在此以后程序员可以运行相应的编译器,通过指定需要编译的文件的名称就可以把相应的源文件(通过一个复杂的过程)转化为机器码了。
[编辑]编译器工作方法
首先编译器进行语法分析,也就是要把那些字符串分离出来。然后进行语义分析,就是把各个由语法分析分析出的语法单元的意义搞清楚。最后生成的是目标文件,我们也称为obj文件。再经过链接器的链接就可以生成最后的可执行代码了。有些时候我们需要把多个文件产生的目标文件进行链接,产生最后的代码。我们把一过程称为交叉链接。
一个现代编译器的主要工作流程如下:
* 源程序(source code)→预处理器(preprocessor)→编译器(compiler)→汇编程序(assembler)→目标程序(object code)→连接器(链接器,Linker)→可执行程序(executables)
工作原理
编译是从源代码(通常为高级语言)到能直接被计算机或虚拟机执行的目标代码(通常为低级语言或机器言)。然而,也存在从低级语言到高级语言的编译器,这类编译器中用来从由高级语言生成的低级语言代码重新生成高级语言代码的又被叫做反编译器。也有从一种高级语言生成另一种高级语言的编译器,或者生成一种需要进一步处理的的中间代码的编译器(又叫级联)。
典型的编译器输出是由包含入口点的名字和地址以及外部调用(到不在这个目标文件中的函数调用)的机器代码所组成的目标文件。一组目标文件,不必是同一编译器产生,但使用的编译器必需采用同样的输出格式,可以链接在一起并生成可以由用户直接执行的可执行程序。
编译器种类
编译器可以生成用来在与编译器本身所在的计算机和操作系统(平台)相同的环境下运行的目标代码,这种编译器又叫做“本地”编译器。另外,编译器也可以生成用来在其它平台上运行的目标代码,这种编译器又叫做交叉编译器。交叉编译器在生成新的硬件平台时非常有用。“源码到源码编译器”是指用一种高级语言作为输入,输出也是高级语言的编译器。例如: 自动并行化编译器经常采用一种高级语言作为输入,转换其中的代码,并用并行代码注释对它进行注释(如OpenMP)或者用语言构造进行注释(如FORTRAN的DOALL指令)。
预处理器(preprocessor)
作用是通过代入预定义等程序段将源程序补充完整。
编译器前端(frontend)
前端主要负责解析(parse)输入的源程序,由词法分析器和语法分析器协同工作。词法分析器负责把源程序中的‘单词’(Token)找出来,语法分析器把这些分散的单词按预先定义好的语法组装成有意义的表达式,语句 ,函数等等。 例如“a = b + c;”前端词法分析器看到的是“a, =, b , +, c;”,语法分析器按定义的语法,先把他们组装成表达式“b + c”,再组装成“a = b + c”的语句。 前端还负责语义(semantic checking)的检查,例如检测参与运算的变量是否是同一类型的,简单的错误处理。最终的结果常常是一个抽象的语法树(abstract syntax tree,或 AST),这样后端可以在此基础上进一步优化,处理。
编译器后端(backend)
编译器后端主要负责分析,优化中间代码(Intermediate representation)以及生成机器代码(Code Generation)。
一般说来所有的编译器分析,优化,变型都可以分成两大类: 函数内(intraproceral)还是函数之间(interproceral)进行。很明显,函数间的分析,优化更准确,但需要更长的时间来完成。
编译器分析(compiler analysis)的对象是前端生成并传递过来的中间代码,现代的优化型编译器(optimizing compiler)常常用好几种层次的中间代码来表示程序,高层的中间代码(high level IR)接近输入的源程序的格式,与输入语言相关(language dependent),包含更多的全局性的信息,和源程序的结构;中层的中间代码(middle level IR)与输入语言无关,低层的中间代码(Low level IR)与机器语言类似。 不同的分析,优化发生在最适合的那一层中间代码上。
常见的编译分析有函数调用树(call tree),控制流程图(Control flow graph),以及在此基础上的变量定义-使用,使用-定义链(define-use/use-define or u-d/d-u chain),变量别名分析(alias analysis),指针分析(pointer analysis),数据依赖分析(data dependence analysis)等等。
上述的程序分析结果是编译器优化(compiler optimization)和程序变形(compiler transformation)的前提条件。常见的优化和变新有:函数内嵌(inlining),无用代码删除(Dead code elimination),标准化循环结构(loop normalization),循环体展开(loop unrolling),循环体合并,分裂(loop fusion,loop fission),数组填充(array padding),等等。优化和变形的目的是减少代码的长度,提高内存(memory),缓存(cache)的使用率,减少读写磁盘,访问网络数据的频率。更高级的优化甚至可以把序列化的代码(serial code)变成并行运算,多线程的代码(parallelized,multi-threaded code)。
机器代码的生成是优化变型后的中间代码转换成机器指令的过程。现代编译器主要采用生成汇编代码(assembly code)的策略,而不直接生成二进制的目标代码(binary object code)。即使在代码生成阶段,高级编译器仍然要做很多分析,优化,变形的工作。例如如何分配寄存器(register allocatioin),如何选择合适的机器指令(instruction selection),如何合并几句代码成一句等等。