GCC初步

http://hi.baidu.com/cubuntu/blog/item/d9fb86f44a2f5669dcc47486.html

 

GCC(GNU Compiler Collection)是Linux下最常用的C语言编译器,是GNU项目中符合ANSI C标准的编译系统,能够编译用C、C++和Object C等语言编写的程序。同时它可以通过不同的前端模块来支持各种语言,如Java、Fortran、Pascal、Modula-3和Ada等。
穿插一个玩笑: GNU意思是GNU’s not Unix而非角马。然而GNU还是一个未拆分的连词,这其实是一个源于hacker的幽默:GNU是一个回文游戏,第一个字母G是凑数的,你当然可以叫他做ANU或者BNU。
下面开始。

一.CC编译程序过程分四个阶段
◆ 预处理(Pre-Processing)
◆ 编译(Compiling)
◆ 汇编(Assembling)
◆ 链接(Linking)

Linux程序员可以根据自己的需要让GCC在编译的任何阶段结束转去检查或使用编译器在该阶段的输出信息,或者对最后生成的二进制文件进行控制,以便通 过加入不同数量和种类的调试代码来为今后的调试做好准备。如同其他的编译器,GCC也提供了灵活而强大的代码优化功能,利用它可以生成执行效率更高的代 码。
GCC提供了30多条警告信息和三个警告级别,使用它们有助于增强程序的稳定性和可移植性。此外,GCC还对标准的C和C++语言进行了大量的扩展,提高程序的执行效率,有助于编译器进行代码优化,能够减轻编程的工作量。


二.简单编译命令
我们以Hello world程序来开始我们的学习。代码如下:

/* hello.c */
#include <stdio.h>
int main(void)
{
printf ("Hello world!/n");
return 0;
}

1. 执行如下命令:$ gcc -o hello hello.c
运行如下 : $ ./hello
输出: Hello,world!
2. 我们也可以分步编译如下:
(1) $ gcc –E hello.c -o hello.i
//预处理结束
//这时候你看一下hello.i ,可以看到插进去了很多东西。
(2) $ gcc –S hello.i
//生成汇编代码后结束
(3) $ gcc –c hello.c
或者:
$ gcc -c hello.c –o hello.o
或者:
$ gcc -c hello.i -o hello.o
//编译结束
//生成 hello.o文件
(4) $ gcc hello.o –o hello.o
或者:
$ gcc –o hello hello.c
//链接完毕,生成可执行代码

3. 我们可以把几个文件一同编译生成同一个可执行文件。
比如:一个工程有main.c foo.c def.c生成foo的可执行文件。
编译命令如下:
$ gcc –c main.c foo.c def.c –o foo
或者:
$ gcc –o foo main.c foo.c def.c
三.库依赖
函数库是一些头文件(.h)和库文件(.so或者.a)的集合。Linux下的大多数函数都默认将头文件放到/usr/include/目录下,而库文件则放到/usr/lib/目录下,但并非绝对如此。因此GCC设有添加头文件和库文件的编译选项开关。
1. 添加头文件:-I
例如在/home/work/include/目录下有编译foo.c所需头文件def.h,为了让GCC能找到它们,就需要使用-I选项:
$ gcc foo.c -I /home/work/include/def.h -o foo
2. 添加库文件:-L
例如在/home/work/lib/目录下有链接所需库文件libdef.so,为了让GCC能找到它们,就需要使用-L选项:
$ gcc foo.c –L /home/work/lib –ldef.a –o foo
说明:-l选项指示GCC去连接库文件libdef.so。Linux下的库文件命名有一个约定,即库文件以lib三个字母开头,因为所有的库文件都遵循这个约定,故在用-l选项指定链接的库文件名时可以省去lib三个字母。
[题外语]
Linux下的库文件分为动态链接库(.so文件)和静态链接库(.a文件)。GCC默认为动态库优先,若想在动态库和静态库同时存在的时候链接静态库需要指明为-static选项。比如上例中如还有一个libdef.a而你想链接libdef.a时候命令如下:
$ gcc foo.c –L /home/work/lib –static –ldef.a –o foo
四.代码优化
GCC提供不同程度的代码优化功能。开关选项是:-On,n取值为0到3。默认为1。-O0表示没有优化,而-O3是最高优化。优化级别越高代码运行越 快,但并不是所有代码都能够加载最高优化,而应该视具体情况而定。但一般都使用-O2选项,因为它在优化长度、编译时间和代码大小之间,取得了一个比较理 想的平衡点。
以下这段是我摘自别人文章的,说的比较详细:
编译时使用选项-O可以告诉GCC同时减小代码的长度和执行时间,其效果等价于-O1。在这一级别上能够进行的优化类型虽然取决于目标处理器,但一般都会 包括线程跳转(Thread Jump)和延迟退栈(Deferred Stack Pops)两种优化。选项-O2告诉GCC除了完成所有-O1级别的优化之外,同时还要进行一些额外的调整工作,如处理器指令调度等。选项-O3则除了完 成所有-O2级别的优化之外,还包括循环展开和其它一些与处理器特性相关的优化工作。通常来说,数字越大优化的等级越高,同时也就意味着程序的运行速度越 快。
下面通过具体实例来感受一下GCC的代码优化功能,所用程序如清单3所示。
/* optimize.c */
#include <stdio.h>
int main(void)
{
double counter;
double result;
double temp;
for (counter = 0;
counter < 2000.0 * 2000.0 * 2000.0 / 20.0 + 2020;
counter += (5 - 1) / 4) {
temp = counter / 1979;
result = counter;
}
printf("Result is %lf/n", result);
return 0;
}

首先不加任何优化选项进行编译:
# gcc -Wall optimize.c -o optimize
借助Linux提供的time命令,可以大致统计出该程序在运行时所需要的时间:
# time ./optimize
Result is 400002019.000000
real 0m14.942s
user 0m14.940s
sys 0m0.000s
接下去使用优化选项来对代码进行优化处理:
# gcc -Wall -O optimize.c -o optimize
在同样的条件下再次测试一下运行时间:
# time ./optimize
Result is 400002019.000000
real 0m3.256s
user 0m3.240s
sys 0m0.000s
对比两次执行的输出结果不难看出,程序的性能的确得到了很大幅度的改善,由原来的14秒缩短到了3秒。这个例子是专门针对GCC的优化功能而设计的,因此 优化前后程序的执行速度发生了很大的改变。尽管GCC的代码优化功能非常强大,但作为一名优秀的Linux程序员,首先还是要力求能够手工编写出高质量的 代码。如果编写的代码简短,并且逻辑性强,编译器就不会做更多的工作,甚至根本用不着优化。
优化虽然能够给程序带来更好的执行性能,但在如下一些场合中应该避免优化代码:
◆ 程序开发的时候优化等级越高,消耗在编译上的时间就越长,因此在开发的时候最好不要使用优化选项,只有到软件发行或开发结束的时候,才考虑对最终生成的代码进行优化。
◆ 资源受限的时候一些优化选项会增加可执行代码的体积,如果程序在运行时能够申请到的内存资源非常紧张(如
一些实时嵌入式设备),那就不要对代码进行优化,因为由这带来的负面影响可能会产生非常严重的后果。
◆ 跟踪调试的时候在对代码进行优化的时候,某些代码可能会被删除或改写,或者为了取得更佳的性能而进行重组,从而使跟踪和调试变得异常困难。

常见参数:

-E
使用-E选项可以让GCC停止在预处理完成阶段(完成所有#define,#if,#include等替换),输出到标准输出,除非指定-o选项,gcc不再做更多的处理。可使用-o选项生成一个以i结尾的文件(GCC默认将.i文件看成是预处理后的C语言源代码)

-S
停止在汇编阶段,输出.s(汇编语言源码)但不调用as
-c               
停止在连接阶段,输出.o但不调用ld
-o FILE               
指定输出文件名。-o选项不是产生可执行二进制代码的关键选项,它只是为gcc的输出给出一个文件名
-DFOO=BAR       
在命令行定义预处理宏FOO,其值为BAR
-IDIRNAME       
将DIRNAME加入到头文件的搜索目录列表中
-LDIRNAME       
将DIRNAME加入到库文件的搜索目录列表中,缺省情况下gcc 只链接共享库
-static               
链接静态库,即执行静态链接,默认情况下,GCC在链接时优先使用动态链接库
-lFOO               
链接名为libFOO的函数库
-g               
GCC在产生调试符号时,同样采用了分级的思路,开发人员可以通过在-g选项后附加数字1、2或 3来指定在代码中加入调试信息 的多少。默认的级别是2(-g2),此时产生的调试信息包括扩展的符号表、行号、局部或外部变量信息。级别3(-g3)包含级别2中的所有调试信息,以及 源代码中定义的宏。级别1(-g1)不包含局部变量和与行号有关的调试信息,因此只能够用于回溯跟踪和堆栈转储之用。回溯跟踪指的是监视程序在运行过程中 的函数调用历史,堆栈转储则是一种以原始的十六进制格式保存程序执行环境的方法,两者都是经常用到的调试手段
-ggdb               
在可执行程序中包含只有GNU debugger才能使别的达两条是信息
-On(n=1,2,3)               
使用编译选项-On来控制优化代码的生成,其中n是一个代表优化级别的整数。通常来说,数字越大优化的等级越高,同时也就意味着程序的运行速度越快。在linux下一般使用-O2选项,因为它在优化长度、编译时间和代码大小之间,取得了一个比较理想的平衡点
-ansi               
支持ANSI/ISO C的标准语法,取消GNU的语法扩展中与该标准有冲突的部分
(但这一选项并不能保证生成ANSI兼容的代码)
-pedantic       
允许发出ANSI/ISO C标准所列出的所有警告
-pedantic -errors       
允许发出ANSI/ISO C标准所列出的所有错误
-traditional       
支持Kernighan & Ritchie C语法(如用旧式语法定义函数)
-w                
关闭所有警告,建议不要使用此项
-Wall               
允许发出gcc能提供的所有有用的警告,也可以用-W(warning)来标记指定的警告
-werror               
把所有警告转换为错误,以在警告发生时中止编译程       
                      
-MM               
输出一个make兼容的相关列表
-x language
指定编程语言language,如C,C++,Java,Ada,F77,Assembler等
-v               
显示在编译过程的每一步中用到的命令
更详细内容,请参见man gcc.
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值