类文件由编译器
1、安装JDK,配置环境变量。 不将JDK所在的目录配置到系统环境变量中,系统怎么能找到JDK中的编译器,解释器在哪呀?如果不指明JDK的bin文件夹的位置,在shell中是无法找到javac/java命令的。这点就不多说了。 2、编译,解释执行Java程序。【 javac命令/java命令】 (1) Test.java源代码 Java代码 //缺省包,该程序源代所在位置: e:/project/ Test.java public class Test{ ..... public static void main(String[] args){ ..... } } 编译命令: 【javac e:/project/Test.java 】 在e:/project目录下生成了Test.class 注意: ① 如果想要将Test.class生成在指定目录下,可以使用javac -d命令,如【 javac -d c:/ e:/project/Test.java 】 在c:/目录下生成T est.class(即e:/Test.class) ② javac -cp 中的-cp并不是指定Test.java的目录,这一点不要误解了。-cp/-classpath只能是指定类文件(.class文件)的路径。上面的命令不能写成: java -cp e:/project Test.java 解释执行命令 :【 j ava -cp e:/project Test】 将调用解释器执行e:/project中的Test.class字节码。 注意: ① -cp 是指定用户类文件的位置,比如上面的Test.class的位置。这里因为要寻找Test.class类文件,而不是Test.java源代码文件,所以要通过-cp指定。千万没有这样的执行命令: java e:/project/Test (2) Test.java源代码 Java代码//缺省包,但源代码中引用了一个JAR包内的自定义类,这个JAR包位于c:/目录下 import net.single.util.SL; //导入自定义JAR包中的类 public class Test{ private SL aObject=new SL(); //初始化JAR中的SL类 public static void main(String[] args){ ...... } } 编译命令: 【 javac -cp c:/single.jar e:/project/Test.java】 在e:/project目录下生成了Test.class 注意: 如果当前你要编译的java文件中引用了其它的类,但该引用类的.class文件不在当前目录下(或在其他目录下,或在.zip/.jar内),这种情况下就需要在javac命令后面,加上-cp/-classpath参数来指明这些类的位置。 一般来说有三种指定方式: ① 绝对或相对路径:javac -cp c:/single.jar Test .java 或 javac -cp ../single.jar Test .java (其中.. 表示上一级目录)
② 系统变量:javac -cp %CLASSPATH% Test .java (其中:%CLASSPATH%表示使用系统变量CLASSPATH的值进行查找,这里假设single.jar的路径就包含在CLASSPATH系统变量中) ③ 当前目录: javac -cp ./single.jar Test.java (其中. 表示当前目录) 解释执行命令 :【 java -cp c:/single.jar;e:/project Test】 注意: ① -cp的路径不仅指定了所需要的single.jar的位置,还必须指出编译后的Test.class的位置。 ② 类路径中的不同项目要用分隔符区分,Unix系统的分隔符是冒号(:),Windows的是分号(;) (3) Test.java 源代码 Java代码//该类在net.single包中,类中没有引入其他目录下的自定义类 package net.single; public class Test{ ..... public static void main(String[] args){ ..... } } 编译命令:【javac -d . e:/project/Test.java 】 注意: ① 如果没有-d而直接编译javac e:/project/Test.java。将会在 e:/project 目录下直接生成一个Test.class,但此Test.class无法解释执行,因为它实际上在e.single包中。所以必须将包一起编译出来,这里用了-d参数。 ② 上面的编译结果将在e:/目录下 自动根据包的结构形式创建文件目录,e:/net/single/Test.class 解释执行命令 :【java -cp e:/ net.single.Test 】 现在我们总结一下: [a.] 没有IDE环境,编译一个大型项目是很困难的,因为必须把需要被其他类引用的类先编译,而且最好把包结构一起编译出来。所以一般命令格式如下: 编译: javac -cp (需要引入的类文件路径1;需要引入的类文件路径2;....) -d (编译出的类文件存放的位置目录) (待编译文件路径) 执行: java -cp (需要解释执行的类文件路径) (带包的类文件) 例:现在要编译一个类源码: Test.java,其中该类位于E:/project/下 (1. Test源代码中使用了一个JAR包中的类,这个single.jar包位于C:/目录下。 (2. Test源代码中使用了一个自定义类Content,这个类的源代码Content.java位于E:/下 (3. Test所在包为net.single,Content所在包为net.single.cont 解决: 步1:由于Test使用了Content类,所以必须先编译Content,而且Content类在E:/目录下,而且 属于包net.single.cont 编译命令: javac -d . e:/Content.java 编译结果: 在Content.java的当前目录下生成了一个 net/single/cont/Content.class 文件(带包结构),即e:/net/single/cont/Content.class 步2:编译Test类,并指明所引入的single.jar包和Content.class的位置 编译命令: javac -cp c:/single.jar;e:/net/single/cont -d . e:/project/Test.java 编译结果: 在Test的上一级目录下生成了一个 net/single/Test.class 文件,即e:/ net/single/Test.class 步3:解释执行Test.class 执行命令: java -cp c:/single.jar;e:/ net.single.Test 3、编译器,虚拟机如何定位到类的 Java代码package net.single; import java.util.*; import net.single.util.*; public class Test{ //SingleUtil类在c:/single.jar中的net.single.util包下 private SingleUtil sut=new SingleUtil(); } 编译命令: javac -cp c:/single.jar -d . e:/project/Test.java 编译器首先找到e:/project/Test.java。然后对Test源代码进行编译,当编译到创建SingleUtil类对象的语句时,编译器要开始寻找SingleUtil.class的位置。编译器首先查找包含这个类的所有包的位置,并查询所有的import指令,确定其中是否包含了被引用了的类。 如上面的Test.java,编译器将试图查找java.lang.SingleUtil,java.util.SingleUtil,net.single.util.SingleUtil以及当前包中的SingleUtil(即net.single.SingleUtil)。编译器将在三个部分中查找类文件: (1) 在JDK的lib目录下的标准类库文件中查找java.lang,java.util和net.single.util包。显然只能找到java.lang和java.util包。然后在这两个包中查找SingleUtil类文件。当然是找不到的。 (2) 在编译命令中-cp参数表明的类路径(C:/single.jar)下查找java.lang,java.util和net.single.util包。显然只能找到net.single.util包,然后在里面找到SingleUtil类文件。 (3) 在Test.java的当前目录下查找SingleUtil,也是没有的。 如果没有找到SingleUtil,或者找到多个SingleUtil。
㈡ C语言程序编译后产生哪些类型的文件这些文件的作用是什么
1、以GCC编译器为例,可以分为四步。
第一步是预处理,包括语法检查等工作。
gcc
-P
abc.c
第二步由源程序生产汇编语言代码。
gcc
-S
abc.c
会生成abc.s文件,这个文件里就是汇编代码。
第三步编译器生成目标代码,一个源文件生成一个目标代码。
gcc
-c
abc.c
会生成abc.o
第四步连接器从目标代码生成可执行文件。
gcc
abc.o
2、目标代码包括机器码和符号表(函数及变量名)。连接器的主要作用是通过符号表在库文件和其他模块中找到在目标代码中引入或未定义的符号(函数及变量名),将几个目标代码合成可执行文件。
㈢ C#类被编译之后成为什么文件
类被编译不会生成文件,编译器也会组织你进行编译。
如果你想做dll,要创建dll工程
㈣ eclipse类文件编译器源文件找不到 80分答案!!!
在网上找到的,希望对你有帮助.
用Eclipse平台进行C/C++开发
关键词: eclipse
我们将概述如何在 C/C++ 开发项目中使用 Eclipse 平台。尽管 Eclipse 主要是一个 Java 开发环境,但其体系结构确保了对其它编程语言的支持。在本文中,您将学习如何使用 C/C++ 开发工具箱(C/C++ Development Toolkit,CDT),它是可用于 Eclipse 的最佳 C/C++ 工具箱。
C 和 C++ 语言都是世界上最流行且使用最普遍的编程语言,因此 Eclipse 平台(Eclipse Platform)提供对 C/C++ 开发的支持一点都不足为奇。因为 Eclipse 平台只是用于开发者工具的一个框架,它不直接支持 C/C++;它使用外部插件来提供支持。本文将向您演示如何使用 CDT — 用于 C/C++ 开发的一组插件。CDT 项目致力于为 Eclipse 平台提供功能完全的 C/C++ 集成开发环境(Integrated Development Environment,IDE)。虽然该项目的重点是 Linux,但它在可使用 GNU 开发者工具的所有环境(包括 Win32(Win 95/98/Me/NT/2000/XP)、QNX Neutrino 和 Solaris 平台)中都能工作。
CDT 是完全用 Java 实现的开放源码项目(根据 Common Public License 特许的),它作为 Eclipse SDK 平台的一组插件。这些插件将 C/C++ 透视图添加到 Eclipse 工作台(Workbench)中,现在后者可以用许多视图和向导以及高级编辑和调试支持来支持 C/C++ 开发。
由于其复杂性,CDT 被分成几个组件,它们都采用独立插件的形式。每个组件都作为一个独立自主的项目进行运作,有它自己的一组提交者、错误类别和邮件列表。但是,所有插件都是 CDT 正常工作所必需的。下面是 CDT 插件/组件的完整列表:
主 CDT 插件(Primary CDT plug-in)是“框架”CDT 插件。
CDT 功能 Eclipse(CDT Feature Eclipse)是 CDT 功能组件(Feature Component)。
CDT 核心(CDT Core)提供了核心模型(Core Model)、CDOM 和核心组件(Core Component)。
CDT UI 是核心 UI、视图、编辑器和向导。
CDT 启动(CDT Launch)为诸如编译器和调试器之类的外部工具提供了启动机制。
CDT 调试核心(CDT Debug Core)提供了调试功能。
CDT 调试 UI(CDT Debug UI)为 CDT 调试编辑器、视图和向导提供了用户界面。
CDT 调试 MI(CDT Debug MI)是用于与 MI 兼容的调试器的应用程序连接器。
现在,让我们研究一下如何在实际应用程序中使用这些组件。图 1 显示了 Eclipse 中的 C/C++ 项目:
图 1. 在带有 CDT 插件的 Eclipse 中编辑 C/C++ 项目
安装和运行 CDT
在下载和安装 CDT 之前,首先必需确保 GNU C 编译器(GNU C compiler,GCC)以及所有附带的工具(make、binutil 和 GDB)都是可用的。如果正在运行 Linux,只要通过使用适用于您分发版的软件包管理器来安装开发软件包。在 Windows 平台上,将需要安装 Cygwin 工具箱。Cygwin 是用于 Windows 的类 UNIX 环境,它包括 GCC 移植以及所有必需的开发工具,包括 automake 和 GNU 调试器(GNU Debugger,GDB)。Cygwin 是在 cygwin1.dll 库基础上构建的。Cygwin 的备用解决方案是 Minimalist GNU for Windows(MinGW)。该工具是一组可免费获取、自由分发的特定于 Windows 的头文件和导入库,这些头文件和导入库与 GNU 工具集(它们允许您生成不依赖于任何第三方 DLL 的本机 Windows 程序)结合在一起。如果您想要创建与 POSIX 兼容的 Windows 应用程序,那么 MinGW 是最佳选择。MinGW 甚至可以在 Cygwin 安装之上工作。Solaris 和 QNX 要求您从因特网下载并安装其特定的 GCC、GNU Make binutils 和 GDB 移植。
假设您安装了适当的 Java SDK/JRE 和 Eclipse 平台 SDK,并且它们都正常运行。CDT 以两种“方式”可用:稳定的发行版和试运行版(nightly build)。试运行版未经完全测试,但它们提供了更多的功能并改正了当前错误。安装之前,请检查磁盘上是否存在先前版本的 CDT,如果存在,请确保完全除去它。因为 CDT 没有可用的卸载程序,所以需要手工除去它。为了检查先前版本是否存在,转至 CDT 插件所驻留的目录:eclipse/plugins。接着,除去所有以 org.eclipse.cdt 名称开头的目录。需要做的最后一件事情是从 workspace/.metadata/.plugins 和 features 除去 CDT 元数据目录 or.eclipse.cdt.*。
下一步是下载 CDT 二进制文件。注意:请下载适合于您操作系统的正确的 CDT。遗憾的是,即使 CDT 是用 Java 编写的,它也不是与平台无关的。接着,将归档文件解压到临时目录中,从临时目录将所有插件目录内容都移到 Eclipse plugins 子目录。还需要将 features 目录内容移到 Eclipse features 子目录中。现在,重新启动 Eclipse。Eclipse 再次启动之后,更新管理器将告诉您它发现了更改并询问您是否确认这些更改。现在您将能够看到两个可用的新项目:C 和 C++。
创建新项目
在 Eclipse 中安装 CDT 之后,浏览至 File => New => Project,在那里,您将发现三个新的可用项目类型:C(“Standard C Make Project”)、C++(“Standard C++ Make Project”)和“Convert to C or C++ Projects”。从“Standard Make C++ Project”开始,为您的项目创建源代码文件。在 C/C++ Projects 视图中,单击鼠标右键,然后选择 New => Simple => File。命名您的文件并保存它。您可能会用这种方法创建许多头文件以及 C/C++ 实现代码文件。最后当然是 Makefile,GNU Make 将使用它来构建二进制文件。对该 Makefile 使用常见的 GNU make 语法(请参阅参考资料)。请记住:Makefile 要求您使用 Tab 字符而不是空格来产生缩进行。
您通常会将现有的源代码导入 Eclipse(请参阅图 2)。CDT 为执行这一操作提供了一种便捷的方法:即使用 Import 向导,将文件从文件系统目录复制到工作台。转至主菜单栏,选择 File => Import => File System。单击 Next,打开源目录,选择您想要添加文件的目录。单击 Select All 以选择目录中的所有资源,然后从头到尾检查,取消选择您不打算添加的那些资源。指定将作为导入目标的工作台项目或文件夹。还可以通过从文件系统拖动文件夹和文件并将它们放入 Navigator 视图中,或者通过复制和粘贴来导入文件夹和文件。
图 2. 将现有的源代码导入 CDT 项目
关键的 CDT IDE 功能
CDT IDE 是在 CDT UI 插件所提供的通用可扩展编辑器基础上构建的。然而,该模块仍处于开发阶段,所以它仍缺少一些重要的实用程序,如类浏览器或语言文档浏览器。CDT IDE 的主要功能是:
语法突出显示:CDT IDE 识别 C/C++ 语法,并为语法突出显示提供了完全可配置的代码着色以及代码格式化功能:
图 3. 不成功的编译之后突出显示的语法错误标记
提纲:Outline 窗口模块提供了有关出现在源代码中的过程、变量、声明以及函数的快速视图。利用 outline,您可以方便地找到源代码中的适当引用,或者甚至搜索所有项目源代码。
代码辅助:这个代码完成功能类似于可在 Borland C++ Builder 或 MS Visual Studio 中找到的功能。它使用了代码模板,并且只有助于避免愚蠢的语法错误:
图 4. 有助于使用正确的语言语法的代码辅助功能
代码模板:由代码辅助功能使用的代码模板是标准 C/C++ 语言语法结构的定义。您也可以定义自己的代码模板来扩展您自己的快捷键,如用于 author 或 date 关键字的快捷键。在 Window => Preferences => C/C++ => Code Templates 中,可以添加新模板并查看完整的模板列表。也可以将模板作为 XML 文件导出和导入。
图 5. 预定义的 C/C++ 代码模板
代码历史记录:即使您没有使用 CVS 或其它源代码版本管理软件,也可以跟踪项目源代码中的本地更改。在选择的文件上单击鼠标右键,从上下文菜单选择 Compare With => Local History...:
图 6. 用 Local History 功能检查源代码中的更改
构建并运行项目
CDT 提供了一种设置项目构建选项的简单方法。CDT 依赖于三个 GNU 工具:GCC、GDB 和 Make。因此,对用于调试的 GDB 或用于编译的 GCC 和 Make 的依赖要求这些应用程序可用于用户想要使用的平台。大多数 Linux(通常和类 POSIX)源代码软件包使用 autoconf 脚本来检查构建环境,所以您必需运行 configure 命令,该命令在编译之前创建“Makefile”。CDT 没有提供编辑 autoconf 脚本的方法,所以必需手工编写它们;然而,您可以配置构建选项以在编译之前调用 configure 命令。
如果通过调用 make 命令来构建项目,那么缺省设置没问题,但是,如果使用一种更复杂的方法进行构建,则必需在 Build Command 文本框中输入适当的命令(例如,make -f make_it_all)。接下来,在 C/C++ Projects 视图中,选择 C/C++ project,然后单击鼠标右键并选择 Rebuild Project。所有来自 make、编译器和链接程序的编译消息都被重定向到控制台窗口:
图 7. 带编译器输出的控制台窗口
编译成功之后,您或许想要运行您的应用程序。所有用于运行和调试的选项都位于主 Eclipse 菜单的 Run 菜单下。然而,必须在早期定义用于运行项目的选项。可以通过转至主菜单(在那里,有用于运行应用程序的不同概要文件)中的 Run... 选项来完成这一步;例如,可以将一个概要文件用于测试目的,而将另一个概要文件用于运行最终版本。另外,可以定义希望要传递给应用程序的参数,或者可以设置环境变量。其它选项用于设置调试选项,例如使用哪个调试器(GNU GDB 或 Cygwin GDB)。图 8 显示了正在为项目创建运行概要文件(run profile)。
图 8. 为项目创建运行概要文件
当进入 C/C++ Projects 视图,选择您的项目,单击鼠标右键并在 Build Settings 选项卡上选择 Properties 之后,就可以使用更多的用于构建项目的常用选项。这些选项主要影响因遇到编译错误而使构建停止时所发生的情况。
调试 C/C++ 项目
CDT 扩展了标准的 Eclipse Debug 视图,使之具备了调试 C/C++ 代码的功能。Debug 视图允许您在工作台中管理程序的调试或运行。要开始调试当前项目,只要切换到 Debug 视图,您将能够在代码中设置(并在执行过程中随时更改)断点/监测点并跟踪变量和寄存器。Debug 视图显示正在调试的每个目标的暂挂线程的堆栈框架。程序中的每个线程都作为树中的一个节点出现,Debug 视图显示正在运行的每个目标的进程。
Eclipse 通过 CDT 调试 MI(CDT Debug MI)插件(其组件之一)支持与机器接口(Machine Interface,MI)兼容的调试器。但 MI 调试器究竟是什么呢?通常情况下,象 ddd 和 xxgdb之类的第三方 GUI 调试器在实现调试功能时都依赖于 GDB 的命令行接口(Command Line Interface,CLI)。遗憾的是,经过证实该接口非常不可靠。GDB/MI 提供了一种新的面向机器的接口,它非常适合于想要直接解析 GDB 输出的程序。
㈤ 一个Java源程序一般是由哪三个基本部分组成的
一个简单的java程序由java包(如:import java.awt.*)、类声明 、变量、构造方法(可缺省)、方法 这几部分构成。
java程序中至少要有一个类被声明为public。
㈥ 编译器是什么
简单讲,编译器就是将“一种语言(通常为高级语言)”翻译为“另一种语言(通常为低级语言)”的程序。一个现代编译器的主要工作流程:源代码 (source code) → 预处理器 (preprocessor) → 编译器 (compiler) → 目标代码 (object code) → 链接器 (Linker) → 可执行程序 (executables)
高级计算机语言便于人编写,阅读交流,维护。机器语言是计算机能直接解读、运行的。编译器将汇编或高级计算机语言源程序(Source program)作为输入,翻译成目标语言(Target language)机器代码的等价程序。源代码一般为高级语言 (High-level language), 如Pascal、C、C++、Java、汉语编程等或汇编语言,而目标则是机器语言的目标代码(Object code),有时也称作机器代码(Machine code)。
对于C#、VB等高级语言而言,此时编译器完成的功能是把源码(SourceCode)编译成通用中间语言(MSIL/CIL)的字节码(ByteCode)。最后运行的时候通过通用语言运行库的转换,编程最终可以被CPU直接计算的机器码(NativeCode)。
编译是从源代码(通常为高级语言)到能直接被计算机或虚拟机执行的目标代码(通常为低级语言或机器语言)的翻译过程。然而,也存在从低级语言到高级语言的编译器,这类编译器中用来从由高级语言生成的低级语言代码重新生成高级语言代码的又被叫做反编译器。也有从一种高级语言生成另一种高级语言的编译器,或者生成一种需要进一步处理的的中间代码的编译器(又叫级联)。
典型的编译器输出是由包含入口点的名字和地址, 以及外部调用(到不在这个目标文件中的函数调用)的机器代码所组成的目标文件。一组目标文件,不必是同一编译器产生,但使用的编译器必需采用同样的输出格式,可以链接在一起并生成可以由用户直接执行的EXE,
所以我们电脑上的文件都是经过编译后的文件。
㈦ 简述JAVA程序的编辑编译和运行过程
第一步(编译): 创建完源文件之后,程序会先被编译为.class文件。Java编译一个类时,如果这个类所依赖的类还没有被编译,编译器就会先编译这个被依赖的类,然后引用,否则直接引用,这个有点象make。
如果java编译器在指定目录下找不到该类所其依赖的类的.class文件或者.java源文件的话,编译器话报“cant find symbol”的错误。
第二步(运行):java类运行的过程大概可分为两个过程:1、类的加载 2、类的执行。需要说明的是:JVM主要在程序第一次主动使用类的时候,才会去加载该类。也就是说,JVM并不是在一开始就把一个程序就所有的类都加载到内存中,而是到不得不用的时候才把它加载进来,而且只加载一次。
特别说明:java类中所有public和protected的实例方法都采用动态绑定机制,所有私有方法、静态方法、构造器及初始化方法<clinit>都是采用静态绑定机制。而使用动态绑定机制的时候会用到方法表,静态绑定时并不会用到。
(7)类文件由编译器扩展阅读:
Java整个编译以及运行的过程相当繁琐,本文通过一个简单的程序来简单的说明整个流程。
Java代码编译:是由Java源码编译器来完成;
Java字节码的执行:是由JVM执行引擎来完成
Java程序从源文件创建到程序运行要经过两大步骤:
1、源文件由编译器编译成字节码(ByteCode)
2、字节码由java虚拟机解释运行。因为java程序既要编译同时也要经过JVM的解释运行,所以说Java被称为半解释语言( "semi-interpreted" language)。
㈧ 编译器的工作原理
编译 是从源代码(通常为高级语言)到能直接被计算机或虚拟机执行的目标代码(通常为低级语言或机器语言)的翻译过程。然而,也存在从低级语言到高级语言的编译器,这类编译器中用来从由高级语言生成的低级语言代码重新生成高级语言代码的又被叫做反编译器。也有从一种高级语言生成另一种高级语言的编译器,或者生成一种需要进一步处理的的中间代码的编译器(又叫级联)。
典型的编译器输出是由包含入口点的名字和地址, 以及外部调用(到不在这个目标文件中的函数调用)的机器代码所组成的目标文件。一组目标文件,不必是同一编译器产生,但使用的编译器必需采用同样的输出格式,可以链接在一起并生成可以由用户直接执行的EXE,
所以我们电脑上的文件都是经过编译后的文件。
㈨ 什么是编译器
编译器
编译器是一种特殊的程序,它可以把以特定编程语言写成的程序变为机器可以运行的机器码。我们把一个程序写好,这时我们利用的环境是文本编辑器。这时我程序把程序称为源程序。在此以后程序员可以运行相应的编译器,通过指定需要编译的文件的名称就可以把相应的源文件(通过一个复杂的过程)转化为机器码了。
[编辑]编译器工作方法
首先编译器进行语法分析,也就是要把那些字符串分离出来。然后进行语义分析,就是把各个由语法分析分析出的语法单元的意义搞清楚。最后生成的是目标文件,我们也称为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),如何合并几句代码成一句等等。