>
编译器:GCC 编译器:GCC 64成员

GCC初学者请进

零点钟 2011-11-07
在此我冒昧的使用维基百科中对于GCC的总括性介绍。【记住:知识是一点一滴的积累的,如果你正的想学习一定要耐心读完这篇博客。我想它足以让一个初学者入门!】
GCC(GNU Compiler Collection,GNU编译器套装),是一套由GNU开发的编程语言编译器。它是一套以GPL及LGPL许可证所发行的自由软件,也是GNU计划的关键部分,亦是自由的类Unix及苹果电脑Mac OS X 操作系统的标准编译器。GCC(特别是其中的C语言编译器)也常被认为是跨平台编译器的事实标准。

GCC原名为GNU C语言编译器(GNU C Compiler),因为它原本只能处理C语言。GCC很快地扩展,变得可处理C++。之后也变得可处理Fortran、Pascal、Objective-C、Java、Ada,以及Go与其他语言。

结构
GCC的外部接口长得像一个标准的Unix编译器。用户在命令行下键入gcc之程序名,以及一些命令参数,以便决定每个输入文件使用的个别语言编译器,并为输出代码使用适合此硬件平台的汇编语言编译器,并且选择性地运行连接器以制造可运行的程序。

每个语言编译器都是独立程序,此程序可处理输入的源代码,并输出汇编语言码。全部的语言编译器都拥有共通的中介架构:一个前端解析符合此语言的源代码,并产生一抽象语法树,以及一翻译此语法树成为GCC的暂存器...<myinc.h>< >
在此我冒昧的使用维基百科中对于GCC的总括性介绍。【记住:知识是一点一滴的积累的,如果你正的想学习一定要耐心读完这篇博客。我想它足以让一个初学者入门!】
GCC(GNU Compiler Collection,GNU编译器套装),是一套由GNU开发的编程语言编译器。它是一套以GPL及LGPL许可证所发行的自由软件,也是GNU计划的关键部分,亦是自由的类Unix及苹果电脑Mac OS X 操作系统的标准编译器。GCC(特别是其中的C语言编译器)也常被认为是跨平台编译器的事实标准。

GCC原名为GNU C语言编译器(GNU C Compiler),因为它原本只能处理C语言。GCC很快地扩展,变得可处理C++。之后也变得可处理Fortran、Pascal、Objective-C、Java、Ada,以及Go与其他语言。

结构
GCC的外部接口长得像一个标准的Unix编译器。用户在命令行下键入gcc之程序名,以及一些命令参数,以便决定每个输入文件使用的个别语言编译器,并为输出代码使用适合此硬件平台的汇编语言编译器,并且选择性地运行连接器以制造可运行的程序。

每个语言编译器都是独立程序,此程序可处理输入的源代码,并输出汇编语言码。全部的语言编译器都拥有共通的中介架构:一个前端解析符合此语言的源代码,并产生一抽象语法树,以及一翻译此语法树成为GCC的暂存器转换语言《RTL》的后端。编译器优化与静态代码解析技术(例如FORTIFY_SOURCE[1],一个试图发现缓存溢出《buffer overflow》的编译器)在此阶段应用于代码上。最后,适用于此硬件架构的汇编语言代码以Jack Davidson与Chris Fraser发明的算法产出。
几乎全部的GCC都由C写成,除了Ada前端大部分以Ada写成。

前端接口
前端的功能在于产生一个可让后端处理之语法树。此语法解析器是手写之递归语法解析器。
直到最近,程序的语法树结构尚无法与欲产出的处理器架构脱钩。而语法树的规则有时在不同的语言前端也不一样,有些前端会提供它们特别的语法树规则。

在2005年,两种与语言脱钩的新型态语法树纳入GCC中。它们称为GENERIC与GIMPLE。语法解析变成产生与语言相关的暂时语法树,再将它们转成GENERIC。之后再使用"gimplifier"技术降低GENERIC的复杂结构,成为一较简单的静态唯一形式(Static Single Assignment form,SSA)基础的GIMPLE形式。此形式是一个与语言和处理器架构脱钩的全局优化通用语言,适用于大多数的现代编程语言。

中介接口
一般编译器作者会将语法树的优化放在前端,但其实此步骤并不看语言的种类而有不同,且不需要用到语法解析器。因此GCC作者们将此步骤归入通称为中介阶段的部分里。此类的优化包括消解死码、消解重复运算与全局数值重编码等。许多优化技巧也正在实现中。

后端接口
GCC后端的行为因不同的前处理器宏和特定架构的功能而不同,例如不同的字符尺寸、调用方式与大小尾序等。后端接口的前半部利用这些消息决定其RTL的生成形式,因此虽然GCC的RTL理论上不受处理器影响,但在此阶段其抽象指令已被转换成目标架构的格式。
GCC的优化技巧依其释出版本而有很大不同,但都包含了标准的优化算法,例如循环优化、运行绪跳跃、共通程序子句消减、指令调度等等。而RTL的优化由于可用的情形较少,且缺乏较高级的信息,因此比较起近来增加的GIMPLE语法树形式[2],便显得比较不重要。
后端经由一重读取步骤后,利用描述目标处理器的指令集时所取得的信息,将抽象暂存器替换成处理器的真实暂存器。此阶段非常复杂,因为它必须关照所有GCC可移植平台的处理器指令集的规格与技术细节。
后端的最后步骤相当公式化,仅仅将前一阶段得到的汇编语言码借由简单的副函数转换其暂存器与存储器位置成相对应的机器码。

替GCC程序除错
为GCC除错的首选工具当然是GNU除错器(gdb)。其他特殊用途的除错工具是Valgrind,用以发现存储器泄漏 (Memory leak)。而GNU测量器(gprof)可以得知程序中某些函数花费多少时间,以及其调用频率;此功能需要用户在编译时选定测量《profiling》选项。

基本规则
  gcc所遵循的部分约定规则:
  .c为后缀的文件,C语言源代码文件;
  .a为后缀的文件,是由目标文件构成的档案库文件;
  .C,.cc或.cxx 为后缀的文件,是C++源代码文件;
  .h为后缀的文件,是程序所包含的头文件;
  .i 为后缀的文件,是已经预处理过的C源代码文件;
  .ii为后缀的文件,是已经预处理过的C++源代码文件;
  .m为后缀的文件,是Objective-C源代码文件;
  .o为后缀的文件,是编译后的目标文件;
  .s为后缀的文件,是汇编语言源代码文件;
  .S为后缀的文件,是经过预编译的汇编语言源代码文件。

基本用法
  在使用Gcc编译器的时候,我们必须给出一系列必要的调用参数和文件名称。GCC编译器的调用参数大约有100多个,其中多数参数我们可能根本就用不到,这里只介绍其中最基本、最常用的参数。
  GCC最基本的用法是∶gcc [options] [filenames]
  其中options就是编译器所需要的参数,filenames给出相关的文件名称。
  -c,只编译,不连接成为可执行文件,编译器只是由输入的.c等源代码文件生成.o为后缀的目标文件,通常用于编译不包含主程序的子程序文件。
  -o output_filename,确定输出文件的名称为output_filename,同时这个名称不能和源文件同名。如果不给出这个选项,gcc就给出预设的可执行文件a.out。
  -g,产生符号调试工具(GNU的gdb)所必要的符号资讯,要想对源代码进行调试,我们就必须加入这个选项。
  -O,对程序进行优化编译、连接,采用这个选项,整个源代码会在编译、连接过程中进行优化处理,这样产生的可执行文件的执行效率可以提高,但是,编译、连接的速度就相应地要慢一些。
  -O2,比-O更好的优化编译、连接,当然整个编译、连接过程会更慢。
  -Idirname,将dirname所指出的目录加入到程序头文件目录列表中,是在预编译过程中使用的参数。C程序中的头文件包含两种情况∶
  A)#include <myinc.h>
  B)#include “myinc.h”
  其中,A类使用尖括号(< >),B类使用双引号(“ ”)。对于A类,预处理程序cpp在系统预设包含文件目录(如/usr/include)中搜寻相应的文件,而B类,预处理程序在目标文件的文件夹内搜索相应文件。

执行过程
  虽然我们称Gcc是C语言的编译器,但使用gcc由C语言源代码文件生成可执行文件的过程不仅仅是编译的过程,而是要经历四个相互关联的步骤∶预处理(也称预编译,Preprocessing)、编译(Compilation)、汇编(Assembly)和链接(Linking)。
  命令gcc首先调用源文件进行预处理,在预处理过程中,对源代码文件中的文件包含(include)、预编译语句(如宏定义define等)进行分析。接着调用cc1进行编译,这个阶段根据输入文件生成以.o为后缀的目标文件。汇编过程是针对汇编语言的步骤,调用as进行工作,一般来讲,.S为后缀的汇编语言源代码文件和汇编、.s为后缀的汇编语言文件经过预编译和汇编之后都生成以.o为后缀的目标文件。当所有的目标文件都生成之后,gcc就调用ld来完成最后的关键性工作,这个阶段就是连接。在连接阶段,所有的目标文件被安排在可执行程序中的恰当的位置,同时,该程序所调用到的库函数也从各自所在的档案库中连到合适的地方。但GCC 的实际操作上,它可以把这三个步骤合并为一个步骤来执行。下面我们以C语言为例来谈一下不同阶段的输入和输出情况。

在预处理阶段,输入的是C语言的源文件, 通常为*.c。它们通常带有.h之类头文件的包含文件。这个阶段主要处理源文件中的#ifdef、 #include和#define命令。该阶段会生成一个中间文件*.i,但实际工作中通常不用专门生成这种文件,因为基本上用不到;若非要生成这种文件 不可,可以利用下面的示例命令:
gcc -E test.c -o test.i

在编译阶段,输入的是中间文件*.i,编 译后生成汇编语言文件*.s 。这个阶段对应的GCC命令如下所示:
GCC -S test.i -o test.s

在汇编阶段,将输入的汇编文件*.s转换 成机器语言*.o。这个阶段对应的GCC命令如下所示:
GCC -c test.s -o test.o

最后,在连接阶段将输入的机器代码文 件*.s(与其它的机器代码文件和库文件)汇集成一个可执行的二进制代码文件。这一步骤,可以利用下面的示例命令完成:
GCC test.o -o test
上面介绍了GCC编译过程的四个阶段以及 相应的命令。
下面进一步介绍常用的GCC的模式
这里介绍GCC追常用的两种模式:编译模 式和编译连接模式。下面以一个例子来说明各种模式的使用方法。为简单起见,假设我们全部的源代码都在一个文件test.c中,要想把这个源文件直接编译成 可执行程序,可以使用以下命令:
$ gcc test.c -o test
这里test.c是源文件,生成的可执行代码存放在一个名为test 的文件中(该文件是机器代码并且可执行)。-o 是生成可执行文件的输出选项。
如果我们只想让源文件生成目标文件(给文件虽然也是机器代码但不可执行),可以使用标记-c ,详细命令如下所示:
$ gcc -c test.c
默认情况下,生成的目标文件被命名为 test.o,但我们也可以为输出文件指定名称,如下所示:
$ gcc -c test.c -o mystest.o
上面这条命令将编译后的目标文件命名为 mytest.o,而不是默认的test.o。
迄今为止,我们谈论的程序仅涉及到一个源 文件。而现实中,一个程序的源代码通常包含在多个源文件之中,这并不复杂,如下:
$ gcc -o test first.c second.c third.c
该命令将同时编译 三个源文件,即first.c、second.c和 third.c,然后将它们连接成一个可执行程序,名为test。

注意:要生成可执行程序时,一个程序无论有有一个源文件还是多个源文件,所有被编译和连接的源文件中必须有且仅有一个main函数,因为main函数是该程序的入口点(换句话说,当系统 调用该程序时,首先将控制权授予程序的main函数)。但如果仅仅是把源文件编译成目标文件的时候,因为不会进行连接,所以main函数不是必需的。



【其中部分来源于wiki,就算作为入门吧】
4
显示全文

查看更多有趣的豆瓣小组

回应 (5条) 只看楼主

  • 零点钟
    大家顶一顶呀
  • simpid
    哈哈,给力的小组!
  • 零点钟
    说明一下,其中部分来源于wiki,说明一下来源
  • 够给力。。。
  • 楠楠
    给力,组内动起来啊
添加回应

更多相关帖子

推荐小组

值得一读

    豆瓣
    我们的精神角落
    免费下载 iOS / Android 版客户端