GNU编译器套件(GNU Compiler Collection)包括C、C++、Objective-C、Fortran、Java、Ada和Go语言的前端,也包括了这些语言的库(如libstdc++、libgcj等等)。GCC的初衷是为GNU作业系统专门编写的一款编译器。GNU系统是彻底的自由软体。此处,“自由”的含义是它尊重用户的自由。
基本介绍
- 软体名称GNU Compiler Collection
- 开发商Free Software Foundation (自由软体基金会)
- 软体平台类Unix作业系统
- 软体版本8.3.0
- 更新时间2019-02-22
- 软体语言多国语言
- 软体授权GNU通用公共许可证(GNU GPL)
- 编写语言C/C++
创作背景
GCC(GNU Compiler Collection,GNU编译器套件),是由 GNU 开发的程式语言编译器。它是以GPL许可证所发行的自由软体,也是 GNU计画的关键部分。GCC原本作为GNU作业系统的官方编译器,现已被大多数类Unix作业系统(如Linux、BSD、Mac OS X等)採纳为标準的编译器,GCC同样适用于微软的Windows。GCC是自由软体过程发展中的着名例子,由自由软体基金会以GPL协定发布。
GCC 原名为 GNU C 语言编译器(GNU C Compiler),因为它原本只能处理 C语言。GCC 很快地扩展,变得可处理 C++。后来又扩展能够支持更多程式语言,如Fortran、Pascal、Objective-C、Java、Ada、Go以及各类处理器架构上的彙编语言等,所以改名GNU编译器套件(GNU Compiler Collection)。
结构
GCC的外部接口长得像一个标準的Unix编译器。使用者在命令列下键入gcc之程式名,以及一些命令参数,以便决定每个输入档案使用的个别语言编译器,并为输出程式码使用适合此硬体平台的组合语言编译器,并且选择性地执行连线器以製造可执行的程式。
每个语言编译器都是独立程式,此程式可处理输入的原始码,并输出组合语言码。全部的语言编译器都拥有共通的中介架构一个前端解析符合此语言的原始码,并产生一抽象语法树,以及一翻译此语法树成为GCC的暂存器转换语言〈RTL〉的后端。编译器最佳化与静态程式码解析技术(例如FORTIFY_SOURCE,一个试图发现缓冲区溢位〈buffer overflow〉的编译器)在此阶段套用于程式码上。,适用于此硬体架构的组合语言程式码以Jack Davidson与Chris Fraser发明的算法产出。
几乎全部的GCC都由C写成,除了Ada前端大部分以Ada写成。
前端接口
前端的功能在于产生一个可让后端处理之语法树。此语法解析器是手写之递归语法解析器。
直到2004年,程式的语法树结构尚无法与欲产出的处理器架构脱钩。而语法树的规则有时在不同的语言前端也不一样,有些前端会提供它们特别的语法树规则。
在2005年,两种与语言脱钩的新型态语法树纳入GCC中。它们称为GENERIC与GIMPLE。语法解析变成产生与语言相关的暂时语法树,再将它们转成GENERIC。之后再使用"gimplifier"技术降低GENERIC的複杂结构,成为一较简单的静态唯一形式(Static Single Assignment form,SSA)基础的GIMPLE形式。此形式是一个与语言和处理器架构脱钩的全域最佳化通用语言,适用于大多数的现代程式语言。
中介接口
一般编译器作者会将语法树的最佳化放在前端,但其实此步骤并不看语言的种类而有不同,且不需要用到语法解析器。GCC作者们将此步骤归入通称为中介阶段的部分里。此类的最佳化包括消解死码、消解重複运算与全域数值重编码等。许多最佳化技巧也正在实作中。
后端接口
GCC后端的行为因不同的前处理器宏和特定架构的功能而不同,例如不同的字元尺寸、呼叫方式与大小尾序等。后端接口的前半部利用这些讯息决定其RTL的生成形式,虽然GCC的RTL理论上不受处理器影响,但在此阶段其抽象指令已被转换成目标架构的格式。
GCC的最佳化技巧依其释出版本而有很大不同,但都包含了标準的最佳化算法,例如循环最佳化、执行绪跳跃、共通程式子句消减、指令排程等等。而RTL的最佳化由于可用的情形较少,且缺乏较高阶的资讯,相比较起来,增加的GIMPLE语法树形式,便显得比较不重要。
后端经由一次重读取步骤后,利用描述目标处理器的指令集时所取得的信息,将抽象暂存器替换成处理器的真实暂存器。此阶段非常複杂,因为它必须关注所有GCC可移植平台的处理器指令集的规格与技术细节。
后端的步骤相当公式化,仅仅将前一阶段得到的彙编语言代码藉由简单的子例程转换其暂存器与记忆体位置成相对应的机器码。
基本用法
在使用GCC编译器的时候,我们必须给出一系列必要的调用参数和档案名称称。GCC编译器的调用参数大约有100多个,这里只介绍其中最基本、最常用的参数。具体可参考GCC Manual。
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类,预处理程式在目标档案的资料夹内搜寻相应档案。
-v gcc执行时执行的详细过程,gcc及其相关程式的版本号
原版gcc manual该选项英文解释
Print (on standard error output) the commands executed to run the stages of compilation. Also print the version number of the compiler driver program and of the preprocessor and the compiler proper.
编译程式时加上该选项可以看到gcc搜寻头档案/库档案时使用的搜寻路径!
基本规则
gcc所遵循的部分约定规则
.c为后缀的档案,C语言原始码档案;
.a为后缀的档案,是由目标档案构成的档案库档案;
.C,.cc或.cxx 为后缀的档案,是C++原始码档案且必须要经过预处理;
.h为后缀的档案,是程式所包含的头档案;
.i 为后缀的档案,是C原始码档案且不应该对其执行预处理;
.ii为后缀的档案,是C++原始码档案且不应该对其执行预处理;
.m为后缀的档案,是Objective-C原始码档案;
.mm为后缀的档案,是Objective-C++原始码档案;
.o为后缀的档案,是编译后的目标档案;
.s为后缀的档案,是彙编语言原始码档案;
.S为后缀的档案,是经过预编译的彙编语言原始码档案。
语言支持
以2006年5月24日释出的4.1.1版为準,本编译器版本可处理下列语言
Ada 〈GNAT〉
C 〈GCC〉
C++(G++)
Fortran 〈Fortran 77: G77, Fortran 90: GFORTRAN〉
Java 〈编译器GCJ;解释器GIJ〉
Objective-C 〈GOBJC〉
Objective-C++
先前版本纳入的CHILL前端由于缺乏维护而被废弃。
Fortran前端在4.0版之前是G77,此前端仅支援Fortran 77。在本版本中,G77被废弃而採用更新的GFortran,因为此前端支援Fortran 95。
下列前端依然存在
Modula-2
Modula-3
Pascal
PL/I
D语言
Mercury
VHDL
执行过程
虽然我们称GCC是C语言的编译器,但使用gcc由C语言原始码档案生成执行档的过程不仅仅是编译的过程,而是要经历四个相互关联的步骤∶预处理(也称预编译,Preprocessing)、编译(Compilation)、彙编(Assembly)和连结(Linking)。
命令gcc调用cpp进行预处理,在预处理过程中,对原始码档案中的档案包含(include)、预编译语句(如宏定义define等)进行分析。接着调用cc1进行编译,这个阶段根据输入档案生成以.i为后缀的目标档案。彙编过程是针对彙编语言的步骤,调用as进行工作,一般来讲,.S为后缀的彙编语言原始码档案和彙编、.s为后缀的彙编语言档案经过预编译和彙编之后都生成以.o为后缀的目标档案。当所有的目标档案都生成之后,gcc就调用ld来完成的关键性工作,这个阶段就是连线。在连线阶段,所有的目标档案被安排在可执行程式中的恰当的位置,,该程式所调用到的库函式也从各自所在的档案库中连到合适的地方。
执行过程示例
- 示例代码
#include<stdio.h>int main(void){ printf("hello\n"); return 0;}
- 预编译过程
这个过程处理宏定义和include,去除注释,不会对语法进行检查。
可以看到预编译后,代码从6行扩展到了910行。
gcc -E a.c -o a.icat a.c|wc -l5cat a.i|wc -l910
- 编译过程
这个阶段,检查语法,生成彙编代码。
gcc -S a.i -o a.scat a.s|wc-l59
- 彙编过程
这个阶段,生成目标代码。
此过程生成ELF格式的目标代码。
gcc -c a.s -o a.ofile a.oa.o:ELF64-bitLSBrelocatable,AMDx86-64,version1(SYSV),notstripped
- 连结过程
连结过程。生成可执行代码。连结分为两种,一种是静态连结,一种是动态连结。使用静态连结的好处是,依赖的动态程式库较少,对动态程式库的版本不会很敏感,具有较好的兼容性;缺点是生成的程式比较大。使用动态连结的好处是,生成的程式比较小,占用较少的记忆体。
gcc a.o -o a
程式运行
./ahello
处理器架构
GCC4.1支持下列处理器架构
Alpha
ARM
Atmel AVR
Blackfin
H8/300
IA-32〈x86〉 与x86-64
IA-64例如Itanium
MorphoSys 家族
Motorola 68000
Motorola 88000
MIPS
PA-RISC
PDP-11
PowerPC
System/370,System/390
SuperH
HC12
SPARC
VAX
Renesas R8C/M16C/M32C家族
较不知名的处理器架构也在官方释出版本中支援
A29K
ARC
C4x
CRIS
D30V
DSP16xx
FR-30
FR-V
Intel i960
IP2000
M32R
68HC11
MCORE
MMIX
MN10200
MN10300
NS32K
ROMP
Stormy16
V850
Xtensa
由FSF个别维护的GCC处理器架构
D10V
MicroBlaze
PDP-10
MSP430
Z8000
当GCC需要移植到一个新平台上,通常使用此平台固有的语言来撰写其初始阶段。
程式除错
为 GCC 除错的首选工具是 GNU 除错器。其他特殊用途的除错工具是 Valgrind, 用以发现记忆体漏失 (Memory leak)。而 GNU 测量器 (gprof) 可以得知程式中某些函式花费多少时间,以及其呼叫频率;此功能需要使用者在编译时选定测量〈profiling〉选项。
使用技巧
检查是否在你的机器上安装了GCC,使用命令
可用rpm -q gcc 检查。
如果没有安装,请依序检查并安装下面各RPM
libbinutils
binutils
make
glibc-devel
gcc-cpp
gcc
看下面的例子test.c
#include<stdio.h>int main(){ char str="I like Linux!I advice you to join in the Linux World"; printf("%s",str); return 0;}
使用gcc编译。输入gcc -c test.c得到目标档案test.o.-c命令表示对档案进行编译和彙编。但并不连线。如果再键入gcc -o ../bin/test test.o,那幺将得到名为test的执行档。其实这两步可以一气呵成,gcc ../bin/test test.c.如果程式没有错误就生成了执行档。也许你会觉得基于命令行的编译器比不上如VC之类的集成开发环境,的确gcc的界面要改进,你一旦熟练了就会感到。gcc的效率如此之高。可以告诉大家的是Linux底下强大的C/C++集成开发环境Kdevelop和Vc一样强大,使用了Gcc编译器。
GNU C编译器 即gcc是一个功能强大的ANSI C兼容编译器,你会操作其他作业系统下的一种C编译器,能很快掌握GCC.
1、使用Gcc,Gcc是基于命令行的,使用时通常后跟一些选项和档案名称。Gcc的基本用法如下 gcc [options] [filenames] 命令行选项制定操作将对命令行上的每个给出的档案执行。
2、GCC的常用选项
编译选项gcc有超过100个的编译选项可用。具体的可以使用命令man gcc察看
最佳化选项用GCC编译C/C++代码时,它会试着用最少的时间完成编译并且编译后的代码易于调试。易于调试意味着编译后的代码与原始码有同样的执行顺序,编译后的代码没有经过最佳化。有很多的选项可以告诉GCC在耗费更多编译时间和牺牲易调试性的基础上产生更小更快的执行档。这些选项中最典型的就是-O和-O2。-O选项告诉gcc对原始码进行基本最佳化。-O2选项告诉GCC产生儘可能小的和儘可能快的代码。还有一些很特殊的选项可以通过man gcc察看。
调试和剖析选项GCC支持数种调试剖析选项。在这些选项中最常用的是-g和-pg.-g选项告诉gcc产生能被GNU调试器(如gdb)使用的调试信息,以便调试用户的程式。-pg选项告诉gcc在用户的程式中加入额外的代码,执行时,产生gprof用的剖析信息以显示程式的耗时情况。
3、使用gdb
使用方法在命令行中键入gdb并按回车就可以运行gdb了,启动gdb后,能在命令行上制定很多的选项,也可以下面的方式来运行gdb: gdb filename 用这种方式运行gdb时,能直接指定想要调试的程式。在命令行上健入gdb -h得到一个有关gdb的选项的说明简单列表。
编译代码以供调试,为了使gdb工作,必须使程式在编译时包含调试信息,调试信息包含程式里的每个变数的类型,在执行档里的地址映射以及原始码的行号。gdb利用这些信息使原始码和机器码相关联。
关于gcc的大体就写这幺多吧,更多的信息可以查找帮助,记得学习Linux的一大武器man或者info命令,下次在介绍一下使用C/C++编写大型程式的makefile档案和make命令。
版本发布
2012年03月23日,GCC 首个公开发布版本是在 1987 年由 Richard Stallman 发布的,到今天已经整整 25 年了。为了庆祝 25 周年,GCC 也相应发布了 GCC 4.7.0 版本,这是 GCC 一个全新的重要版本。
GCC 4.7.0 带来了一组关于连结时最佳化 (LTO) 框架可提升伸缩性和降低记忆体使用,据开发者称,在 64 位系统上需要 8G 记忆体来对 Firefox 进行最佳化,用了 LTO 后只需 3G。
就是体验的支持软体事务记忆体,支持更多 C++11 标準,包括原子性、C++11 记忆体模型,用户定义文字、别名声明、构造器委派和可扩展的语法等。
GCC 4.7.0 还改进对 Fortran 的支持,支持 Google Go 1 等等多项改进。
2012年06月14日,GCC 4.7.1 发布了,这是一个 bug 修复版本,主要是 4.7.0 中的一些回归测试发现的问题修复。
2013年04月11日,GCC 4.7.3 发布。
2013年03月22日,GCC 4.8.0发布,进一步加强了对已C++11的支持。并且G++开始支持-std=c++1y选项,用来支持计画于2014年发布的C++11修订版标準(C++14)。
2013年10月16日,GCC 4.8.2发布。提供了对于OpenMP 4.0的支持。
2014年04月22日,GCC发布了4.9.0版本,提供了对C11标準的Generic Selection语法特性(_Generic)的支持以及对多执行绪方面特性的支持。
安装方法
linux中
RedHat中安装
用which gcc命令查看,假如有显示” /usr/bin/gcc”的话说明已经安装了,否则就是没有安装。
这里以redhat 64位 linux为例。将redhat的iso系统镜像挂在到/mnt/cdrom目录下
mount -o loop /data/rhel-server-5.4-x86_64-dvd.iso /mnt/cdrom
进入/mnt/cdrom目录,就可以访问iso镜像中的内容了。
cd /mnt/cdrom
cd Server/
mount -o loop /data/rhel-server-5.4-x86_64-dvd.iso /mnt/cdrom
进入/mnt/cdrom目录,就可以访问iso镜像中的内容了。
cd /mnt/cdrom
cd Server/
安装gcc的依赖包以及gcc,按以下命令依次执行
rpm -ivh binutils-2.17.50.0.6-12.el5.x86_64.rpm
rpm -ivh cpp-4.1.2-46.el5.x86_64.rpm
rpm -ivh kernel-headers-2.6.18-164.el5.x86_64.rpm
rpm -ivh glibc-devel-2.5-42.x86_64.rpm
rpm -ivh glibc-headers-2.5-42.x86_64.rpm
rpm -ivh libgomp-4.4.0-6.el5.x86_64.rpm
rpm -ivh gcc-4.1.2-46.el5.x86_64.rpm
大家在安装rpm包时,由于依赖关係,在安装时会提示“此包依赖其他包xx”,让你先安装其他包,大家按照提示来,提示你先安装哪个包就安装哪个包。
rpm -ivh binutils-2.17.50.0.6-12.el5.x86_64.rpm
rpm -ivh cpp-4.1.2-46.el5.x86_64.rpm
rpm -ivh kernel-headers-2.6.18-164.el5.x86_64.rpm
rpm -ivh glibc-devel-2.5-42.x86_64.rpm
rpm -ivh glibc-headers-2.5-42.x86_64.rpm
rpm -ivh libgomp-4.4.0-6.el5.x86_64.rpm
rpm -ivh gcc-4.1.2-46.el5.x86_64.rpm
大家在安装rpm包时,由于依赖关係,在安装时会提示“此包依赖其他包xx”,让你先安装其他包,大家按照提示来,提示你先安装哪个包就安装哪个包。
Ubuntu中安装
安装4.8版为例
sudo apt-get install gcc-4.8