gcc 编译器
1,在使用gcc编译程序时,编译过程可以细分为4个阶段:
1)预处理
2)编译
3)汇编
4)链接
Linux程序员可以根据自己的需要让gcc在编译的任何阶段结束,检查或使用编译器在该阶段的输出信息,或者对最后生成的二进制文件进行控制,以便通过加入不同数量和种类的调试代码来为今后的调试做好准备。与其他常用的编译器一样,gcc 也提供了灵活而强大的代码优化功能,利用它可以生成执行效率更高的代码。gcc 提供了30多条警告信息和3个警告级别,使用他们有助于增强程序的稳定性和可移植性。
2,使用 gcc
1)gcc 的版本可以使用如下gcc -v命令查看。
2)下面以一个实例来说明如何使用gcc编译器。
a;在文本编辑器中编辑 hello.c,输入下面内容,并保存为hello.c
#include<stdio.h>
int main(int argc,char **argv)
{
printf(&quot;Hello Ubuntu\n&quot;);
}
b;要编译这个程序,只要在命令行下执行如下命令:
lammy@lammy-desktop:~$ gcc hello.c -o hello
lammy@lammy-desktop:~$ ./hello
Hello Ubuntu
c;执行上述命令后,gcc编译器会生成一个名为hello的可执行文件,然后执行./hello就可以看到程序输出的结果了。
d;命令行中gcc表示用gcc来编译源程序,-o 选项表示要求编译器输出的可执行文件名为hello,而hello.c是源程序文件。
f;为了更好的了解gcc的工作过程,可以把上述编译过程分成几个步骤单独进行,并观察每步的运行结果。
第一步是要进行预编译,使用-E参数可以让gcc在预处理结束后停止编译过程:
lammy@lammy-desktop:~$ gcc -E hello.c -o hello.i
此时若查看hello.i文件中的内容,会发现stdio.h的内容确实都插到文件里去了,而且被预处理的宏定义也都做了相应的处理。由于该文档长达
914行,故没有给出。
下一步是将hello.i编译为目标代码,这可以通过使用-c参数来完成:
lammy@lammy-desktop:~$ gcc -c hello.i -o hello.o
gcc默认将.i文件看成是预处理后的C语言源代码,因此上述命令将自动跳过预处理步骤而开始执行编译过程,也可以使用-x参数让gcc从指定的步骤
开始编译。最后一步是将生成的目标文件链接成可执行文件:lammy@lammy-desktop:~$ gcc hello.o -o hello
g;如果整个程序是由多个源文件组成的,比如说一个有1.c和2.c两个源文件组成的程序,为了对它们进行编译,并最终生成可执行程序drive,可以使用下面这条命
令:lammy@lammy-desktop:~$ gcc 1.c 2.c -o drive
此时该命令大致相当于一次执行如下3条命令:
lammy@lammy-desktop:~$ gcc 1.c -o 1.o
lammy@lammy-desktop:~$ gcc 2.c -o 2.o
lammy@lammy-desktop:~$ gcc 1.o 2.o -o drive
h;如果项目中有100个源文件需要编译,并且每个源文件都包含 10 000 行代码,如果依然相上面那样仅用一条gcc命令来完成编译工作,那么和明显将很费时间的。要
解决这个问题,关键是要灵活运用gcc,同时还要借助相make这样的工具。关于make,以后再说。
3)gcc 警告提示功能
a;gcc在编译不符合ANSI/ISO C语言标准的源代码时,如果加上了-pedantic 选项,那么使用了扩展语法的地方将产生相应的警告信息;需要注意的是,-pedantic编译选项并不能保证被编译的程序与ANSI/ISO C语言标准完全兼容,它仅仅用来帮助Linux程序员离这个目标越来越近。
b;除了-pedantic之外,gcc还有一些其他编译选项也能够产生有用的警告信息。这些选项大多以-W开头,其中最有价值的当数-Wall了,使用它能够使gcc产生尽可能多的警告信息。
c;在处理警告方面,另一个常用的编译选项是-Werror,它是要求gcc将所有的警告当成错误进行处理,这在使用自动编译工具时非常有用。如果编译时但上-Werror选项,那么gcc会在产生警告的地方停止编译,从而迫使程序员对自己的代码进行修改。
d;建议在用gcc编译源代码时始终带上-Wall选项,并把他逐渐培养成一种习惯,这对找出常见的隐式编程错误很有帮助。
4)库依赖
a;函数库实际上就是一些头文件(.h)和库文件(.so或者.a)的集合,虽然Linux下大多数函数都默认将头文件放到/usr/include/目录下,而库文件则放到/usr/lib/目录下,但并不是所有的情况都是这样的。
b;gcc采用搜索目录的方法来查找所需要的文件,-I选项可以向gcc的头文件搜索路径中添加新的目录。例如如果在/home/lammy/include/目录下有编译时所需要的头文件,可以使用如下命令:lammy@lammy-desktop:~$ gcc lammy.c -I /home/lammy/include -o lammy
c;同样,如果使用了不在标准位置的库文件,那么可以通过-L选项向gcc的库文件搜索路径中添加新的目录。例如如果在/home/lammy/lib目录下有链接时所需的库文件liblammy.so,可以使用如下命令:lammy@lammy-desktop:~$ gcc lammy.c -L /home/lammy/lib -llammy -o lammy
d;值得详细解释一下-llibrary选项,他指示gcc去连接库文件library。上面的命令指示gcc去连接库文件lammy.so。Linux下的库文件在命名时有一个约定,那就是应该以lib三个字母开头。因此在用-l选项指定连接的库文件名时可以省去lib三个字母。
f;Linux下的库文件分为两大类,分别是动态链接库(通常以.so结尾)和静态链接库(通常以.a结尾),两者的差别仅在于程序执行时所需的代码是运行时动态加载的,还是编译时静态加载的。默认情况下,gcc在链接时优先使用动态链接库,只有当动态库不存在时才考虑静态链接库。如果需要的话可以在编译时加上-static选项,强制使用静态链接库。例如如果在/home/lammy/lib/目录下有链接时所需的库文件libfoo.so和libfoo.a,为了让gcc在链接时只用到静态链接库,可以使用下面的命令:lammy@lammy-desktop:~$ gcc foo.c -L /home/lammy/lib -static -lfoo -o lammy
5)gcc代码优化
gcc提供的代码优化功能非常强大,它通过编译选项-On来控制优化代码的生成,其中n是一个代表优化级别的整数,不同的版本的gcc,n的取值范围及其对应的优化效果并不完全相同,比较典型的范围是从0变化到2或3。
a;选项-O可以告诉gcc同时减小代码的长度和执行时间,其效果等价于-O1。在这一级别上能够进行的优化类型虽然取决于目标处理器,但一般都会包括线程跳转和延迟退栈两种优化。
b;选项-O2告诉gcc除了完成所有-O1级别的优化之外,同时还要进行一些额外的调整工作,如处理器指令调度。
c;选项-O3则除了完成所有-O2级别的优化之外,还包括循环展开和其他一些与处理器特性相关的优化工作。
d;一般数字n越大优化的等级越高,同时意味着程序的运行速度越快。不过建议使用-O2选项,因为它在优化长度、编译时见和代码大小之间取得了一个比较理想的平衡点。
6)加速
在将源代码变成可执行文件的过程中,需要经过许多中间步骤,包含预处理、编译、汇编、连接。这些过程实际上是有不同的程序负责完成的。这样左右一个很明显的缺点,就是gcc在处理每一个源程序时,最终都需要生成好几个临时文件才能完成相应的工作,从而无形中导致处理速度变慢。解决办法是使用Linux提供的一种更加高效的通信方式——管道。它可以用来同时连接两个程序,其中一个程序的输出将直接作为另一个程序的输入,这样就可以避免使用临时文件,但编译时却需要消耗更多的内存。编译过程中使用管道是由gcc的-pipe选项决定的。如:lammy@lammy-desktop:~$ gcc -pipe lammy.c -o lammy |