一、简介
GCC 的意思也只是GNU C Compiler 而已。经过了这么多年的发展,GCC已经不仅仅能支持C 语言;它现在还支持Ada 语言、C++语言、Java语言、ObjectiveC 语言、Pascal语言、COBOL语言,以及支持函数式编程和逻辑编程的Mercury 语言,等等。而GCC 也不再单只是GNU C 语言编译器的意思了,而是变成了GNU Compiler Collection 也即是GNU 编译器家族的意思了。另一方面,说到GCC 对于操作系统平台及硬件平台支持,概括起来就是一句话:无所不在。
二、简单编译
示例程序如下:
//test.c
#include <stdio.h>
int main(void)
{
printf("HelloWorld!\n");
return 0;
}
这个程序,一步到位的编译指令是: gcc test.c -o test
实质上,上述编译过程是分为四个阶段进行的,即预处理(也称预编译,Preprocessing)、编译(Compilation)、汇编(Assembly)和连接(Linking)。
2.1预处理
gcc -E test.c 或 gcc -E test.c -o test.i
可以输出test.i文件中存放着test.c经预处理之后的代码。打开test.i文件,看一看,就明白了。后面那条指令,是直接在命令行窗口中输出预处理后的代码.
gcc的-E选项,可以让编译器在预处理后停止,并输出预处理结果。在本例中,预处理结果就是将stdio.h文件中的内容插入到test.c中了。
2.2编译为汇编代码(Compilation)
预处理之后,可直接对生成的test.i文件编译,生成汇编代码:
gcc -S test.i -o test.s
gcc的-S选项,表示在程序编译期间,在生成汇编代码后,停止,-o输出汇编代码文件。
2.3汇编(Assembly)
对于上一小节中生成的汇编代码文件test.s,gas汇编器负责将其编译为目标文件,如下:
gcc -c test.s -o test.o
2.4连接(Linking)
gcc连接器是gas提供的,负责将程序的目标文件与所需的所有附加的目标文件连接起来,最终生成可执行文件。附加的目标文件包括静态连接库和动态连接库。
对于上一小节中生成的test.o,将其与C标准输入输出库进行连接,最终生成程序test:gcc test.o -o test
在命令行窗口中,执行./test,让它说HelloWorld吧!
三、多个程序文件的编译
通常整个程序是由多个源文件组成的,相应地也就形成了多个编译单元,使用GCC能够很好地管理这些编译单元。假设有一个由test1.c和test2.c两个源文件组成的程序,为了对它们进行编译,并最终生成可执行程序test,可以使用下面这条命令:
gcc test1.c test2.c -o test
如果同时处理的文件不止一个,GCC仍然会按照预处理、编译和链接的过程依次进行。如果深究起来,上面这条命令大致相当于依次执行如下三条命令:
gcc -c test1.c -o test1.o
gcc -c test2.c -o test2.o
gcc test1.o test2.o -o test
四、检错
gcc -pedantic illcode.c -o illcode
-pedantic编译选项并不能保证被编译程序与ANSI/ISOC标准的完全兼容,它仅仅只能用来帮助Linux程序员离这个目标越来越近。或者换句话说,-pedantic选项能够帮助程序员发现一些不符合ANSI/ISO C标准的代码,但不是全部,事实上只有ANSI/ISOC语言标准中要求进行编译器诊断的那些情况,才有可能被GCC发现并提出警告。
除了-pedantic之外,GCC还有一些其它编译选项也能够产生有用的警告信息。这些选项大多以-W开头,其中最有价值的当数-Wall了,使用它能够使GCC产生尽可能多的警告信息。
gcc -Wall illcode.c -o illcode
GCC给出的警告信息虽然从严格意义上说不能算作错误,但却很可能成为错误的栖身之所。一个优秀的Linux程序员应该尽量避免产生警告信息,使自己的代码始终保持标准、健壮的特性。所以将警告信息当成编码错误来对待,是一种值得赞扬的行为!所以,在编译程序时带上-Werror选项,那么GCC会在所有产生警告的地方停止编译,迫使程序员对自己的代码进行修改,如下:
gcc -Werror test.c -o test
五、库文件连接
开发软件时,完全不使用第三方函数库的情况是比较少见的,通常来讲都需要借助许多函数库的支持才能够完成相应的功能。从程序员的角度看,函数库实际上就是一些头文件(.h)和库文件(so、或lib、dll)的集合。。虽然Linux下的大多数函数都默认将头文件放到/usr/include/目录下,而库文件则放到/usr/lib/目录下;Windows所使用的库文件主要放在VisualStido的目录下的include和lib,以及系统文件夹下。但也有的时候,我们要用的库不再这些目录下,所以GCC在编译时必须用自己的办法来查找所需要的头文件和库文件。
例如我们的程序test.c是在linux上使用c连接MySQL,这个时候我们需要去mysql官网下载MySQLConnectors的C库,下载下来解压之后,有一个include文件夹,里面包含mysqlconnectors的头文件,还有一个lib文件夹,里面包含二进制so文件libmysqlclient.so
其中inclulde文件夹的路径是/usr/dev/mysql/include,lib文件夹是/usr/dev/mysql/lib
5.1编译成可执行文件
首先我们要进行编译test.c为目标文件,这个时候需要执行
gcc –c –I /usr/dev/mysql/include test.c –o test.o
5.2链接
最后我们把所有目标文件链接成可执行文件:
gcc –L /usr/dev/mysql/lib –lmysqlclient test.o –o test
Linux下的库文件分为两大类分别是动态链接库(通常以.so结尾)和静态链接库(通常以.a结尾),二者的区别仅在于程序执行时所需的代码是在运行时动态加载的,还是在编译时静态加载的。
5.3强制链接时使用静态链接库
默认情况下,GCC在链接时优先使用动态链接库,只有当动态链接库不存在时才考虑使用静态链接库,如果需要的话可以在编译时加上-static选项,强制使用静态链接库。
在/usr/dev/mysql/lib目录下有链接时所需要的库文件libmysqlclient.so和libmysqlclient.a,为了让GCC在链接时只用到静态链接库,可以使用下面的命令:
gcc –L /usr/dev/mysql/lib –static –lmysqlclient test.o –otest
静态库链接时搜索路径顺序:
ld会去找GCC命令中的参数-L
再找gcc的环境变量LIBRARY_PATH
再找内定目录/lib /usr/lib /usr/local/lib 这是当初compilegcc时写在程序内的
动态链接时、执行时搜索路径顺序:
编译目标代码时指定的动态库搜索路径
环境变量LD_LIBRARY_PATH指定的动态库搜索路径
配置文件/etc/ld.so.conf中指定的动态库搜索路径
默认的动态库搜索路径/lib
默认的动态库搜索路径/usr/lib
有关环境变量:
LIBRARY_PATH环境变量:指定程序静态链接库文件搜索路径
LD_LIBRARY_PATH环境变量:指定程序动态链接库文件搜索路径
六、Linux程序gcc编译步骤:
Gcc编译过程主要的4个阶段:
l 预处理阶段,完成宏定义和include文件展开等工作;(.i)
l 根据编译参数进行不同程度的优化,编译成汇编代码(.s.S)
l 用汇编器把汇编代码进一步生成目标代码(.o)
l 用连接器把生成的目标代码和系统或用户提供的库连接起来,生成可执行文件
格式:
l gcc -E test.c//预处理阶段
l Gcc -S test.c//编译阶段
l Gcc -c test.c//汇编阶段
l Gcc -o test test.c//链接阶段
七、Linux程序gdb调试步骤:
Gdb的功能:
l 设置断点
l 监视程序变量的值
l 程序的单步执行
l 显示、修改变量的值
l 显示、修改寄存器
l 查看程序的堆栈情况
l 远程调试
八、Gdb调试过程:
1、程序经过预处理后,即进入编译阶段,进入编译阶段,首先声明编译:
2、格式:gdb-o test test.c -g
3、进入编译:gdbtest
4、显示需要编译调试的源程序:l(list)//listfilename
5、设置断点:b(break)行号
6、查看设置的断点:infob
7、运行调试程序:run
8、跳到下一个断点:c(continue)
9、单步运行的话使用:n(next)/s(stepinto)跳到函数体//区别在与:next执行函数体,而step不执行函数体
10、调试过程中查看某个变量的变化:printi (每次都要手动设置)//displayi(设置一次一直尾随,直到用“undisplay变量标号” 停止)
11、退出当前的调试使用finish跳出函数
12、清楚断点clear 行号
13、Delete断点信息序号//删除所有断点或设置的要删除的断点
14、退出调试q
15、b num if i==20 设置断点的触发条件
16、conditionnum i==50 改变断点的触发条件
九、使用Pkg-config工具编译
9.1、编译和连接
一般来说,如果库的头文件不在/usr/include 目录中,那么在编译的时候需要用-I 参数指定其路径。由于同一个库在不同系统上可能位于不同的目录下,用户安装库的时候也可以将库安装在不同的目录下,所以即使使用同一个库,由于库的路径的不同,造成了用-I 参数指定的头文件的路径也可能不同,其结果就是造成了编译命令界面的不统一。如果使用-L 参数,也会造成连接界面的不统一。编译和连接界面不统一会为库的使用带来麻烦。
为了解决编译和连接界面不统一的问题,人们找到了一些解决办法。其基本思想就是:事先把库的位置信息等保存起来,需要的时候再通过特定的工具将其中有用的信息提取出来供编译和连接使用。这样,就可以做到编译和连接界面的一致性。其中,目前最为常用的库信息提取工具就是下面介绍的pkg-config。
pkg-config 是通过库提供的一个 .pc 文件获得库的各种必要信息的,包括版本信息、编译和连接需要的参数等。这些信息可以通过pkg-config 提供的参数单独提取出来直接供编译器和连接器使用。
The pkgconfig package contains tools for passing the includepath and/or library paths to build tools during the make file execution.
pkg-config is a function that returns meta information forthe specified library.
The default setting for PKG_CONFIG_PATH is/usr/lib/pkgconfig because of the prefix we use to install pkgconfig. You mayadd to PKG_CONFIG_PATH by exporting additional paths on your system wherepkgconfig files are installed. Note that PKG_CONFIG_PATH is only needed whencompiling packages, not during run-time.
在默认情况下,每个支持pkg-config 的库对应的.pc 文件在安装后都位于安装目录中的lib/pkgconfig 目录下。例如,我们在上面已经将Glib 安装在/opt/gtk 目录下了,那么这个Glib 库对应的.pc 文件是/opt/gtk/lib/pkgconfig 目录下一个叫glib-2.0.pc 的文件:
prefix=/opt/gtk/ exec_prefix= prefixlibdir= {exec_prefix}/lib includedir=${prefix}/include
glib_genmarshal=glib-genmarshal gobject_query=gobject-queryglib_mkenums=glib-mkenums
Name: GLib Description: C Utility Library Version: 2.12.13Libs: -L libdir−lglib−2.0Cflags:−I {includedir}/glib-2.0 -I${libdir}/glib-2.0/include
使用pkg-config 的–cflags 参数可以给出在编译时所需要的选项,而–libs 参数可以给出连接时的选项。例如,假设一个sample.c 的程序用到了Glib 库,就可以这样编译:
$ gcc -c pkg-config --cflags glib-2.0
sample.c
然后这样连接:
$ gcc sample.o -o sample pkg-config --libs glib-2.0
或者上面两步也可以合并为以下一步:
$ gcc sample.c -o sample pkg-config --cflags --libsglib-2.0
可以看到:由于使用了pkg-config 工具来获得库的选项,所以不论库安装在什么目录下,都可以使用相同的编译和连接命令,带来了编译和连接界面的统一。
使用pkg-config 工具提取库的编译和连接参数有两个基本的前提:
- 库本身在安装的时候必须提供一个相应的 .pc 文件。不这样做的库说明不支持pkg-config 工具的使用。 2. pkg-config 必须知道要到哪里去寻找此 .pc 文件。 GTK+ 及其依赖库支持使用 pkg-config 工具,所以剩下的问题就是如何告诉 pkg-config 到哪里去寻找库对应的.pc 文件,这也是通过设置搜索路径来解决的。对于支持pkg-config 工具的GTK+ 及其依赖库来说,库的头文件的搜索路径的设置变成了对.pc 文件搜索路径的设置。.pc文件的搜索路径是通过环境变量PKG_CONFIG_PATH 来设置的,pkg-config将按照设置路径的先后顺序进行搜索,直到找到指定的.pc 文件为止。
安装完Glib 后,在bash 中应该进行如下设置:
exportPKGCONFIGPATH=/opt/gtk/lib/pkgconfig: PKG_CONFIG_PATH
可以执行下面的命令检查是否/opt/gtk/lib/pkgconfig 路径已经设置在PKG_CONFIG_PATH 环境变量中:
echo PKG_CONFIG_PATH
这样设置之后,使用Glib 库的其它程序或库在编译的时候pkg-config 就知道首先要到/opt/gtk/lib/pkgconfig 这个目录中去寻找glib-2.0.pc 了(GTK+和其它的依赖库的.pc 文件也将拷贝到这里,也会首先到这里搜索它们对应的.pc 文件)。之后,通过pkg-config 就可以把其中库的编译和连接参数提取出来供程序在编译和连接时使用。
另外还需要注意的是:环境变量的设置只对当前的终端窗口有效。如果到了没有进行上述设置的终端窗口中,pkg-config将找不到新安装的glib-2.0.pc 文件、从而可能使后面进行的安装(如Glib 之后的Atk 的安装)无法进行。
在我们采用的安装方案中,由于是使用环境变量对GTK+ 及其依赖库进行的设置,所以当系统重新启动、或者新开一个终端窗口之后,如果想使用新安装的GTK+ 库,需要如上面那样重新设置PKG_CONFIG_PATH 和LD_LIBRARY_PATH 环境变量。
这种使用GTK+ 的方法,在使用之前多了一个对库进行设置的过程。虽然显得稍微繁琐了一些,但却是一种最安全的使用GTK+ 库的方式,不会对系统上已经存在的使用了GTK+ 库的程序(比如GNOME 桌面)带来任何冲击。
为了使库的设置变得简单一些,可以把下面的这两句设置保存到一个文件中(比如set_gtk-2.10 文件):
export
PKG_CONFIG_PATH=/opt/gtk/lib/pkgconfig:$PKG_CONFIG_PATH export
LD_LIBRARY_PATH=/opt/gtk/lib:$LD_LIBRARY_PATH 之后,就可以用下面的方法进行库的设置了(其中的 source 命令也可以用 .代替):
$ source set_gtk-2.10
只有在用新版的GTK+ 库开发应用程序、或者运行使用了新版GTK+ 库的程序的时候,才有必要进行上述设置。
如果想避免使用GTK+ 库之前上述设置的麻烦,可以把上面两个环境变量的设置在系统的配置文件中(如/etc/profile)或者自己的用户配置文件中(如~/.bash_profile) ;库的搜索路径也可以设置在/etc/ld.so.conf 文件中,等等。这种设置在系统启动时会生效,从而会导致使用GTK+ 的程序使用新版的GTK+ 运行库,这有可能会带来一些问题。当然,如果你发现用新版的GTK+ 代替旧版没有什么问题的话,使用这种设置方式是比较方便的。加入到~/.bashrc中,例如:PKG_CONFIG_PATH=/opt/gtk/lib/pkgconfig 重启之后: [root@localhost ~]# echo$PKG_CONFIG_PATH /opt/gtk/lib/pkgconfig
9.2、运行时
库文件在连接(静态库和共享库)和运行(仅限于使用共享库的程序)时被使用,其搜索路径是在系统中进行设置的。一般Linux 系统把/lib 和/usr/lib 两个目录作为默认的库搜索路径,所以使用这两个目录中的库时不需要进行设置搜索路径即可直接使用。对于处于默认库搜索路径之外的库,需要将库的位置添加到库的搜索路径之中。设置库文件的搜索路径有下列两种方式,可任选其一使用:
在环境变量 LD_LIBRARY_PATH 中指明库的搜索路径。 2. 在 /etc/ld.so.conf 文件中添加库的搜索路径。 将自己可能存放库文件的路径都加入到/etc/ld.so.conf中是明智的选择添加方法也极其简单,将库文件的绝对路径直接写进去就OK了,一行一个。例如:/usr/X11R6/lib /usr/local/lib /opt/lib
需要注意的是:第二种搜索路径的设置方式对于程序连接时的库(包括共享库和静态库)的定位已经足够了,但是对于使用了共享库的程序的执行还是不够的。这是因为为了加快程序执行时对共享库的定位速度,避免使用搜索路径查找共享库的低效率,所以是直接读取库列表文件/etc/ld.so.cache 从中进行搜索的。/etc/ld.so.cache是一个非文本的数据文件,不能直接编辑,它是根据/etc/ld.so.conf 中设置的搜索路径由/sbin/ldconfig 命令将这些搜索路径下的共享库文件集中在一起而生成的(ldconfig命令要以root 权限执行)。因此,为了保证程序执行时对库的定位,在/etc/ld.so.conf 中进行了库搜索路径的设置之后,还必须要运行/sbin/ldconfig 命令更新/etc/ld.so.cache 文件之后才可以。ldconfig,简单的说,它的作用就是将/etc/ld.so.conf列出的路径下的库文件 缓存到/etc/ld.so.cache以供使用。因此当安装完一些库文件,(例如刚安装好glib),或者修改ld.so.conf增加新的库路径后,需要运行一下/sbin/ldconfig使所有的库文件都被缓存到ld.so.cache中,如果没做,即使库文件明明就在/usr/lib下的,也是不会被使用的,结果编译过程中抱错,缺少xxx库,去查看发现明明就在那放着,搞的想大骂computer蠢猪一个。^_^ 在程序连接时,对于库文件(静态库和共享库)的搜索路径,除了上面的设置方式之外,还可以通过-L 参数显式指定。因为用-L 设置的路径将被优先搜索,所以在连接的时候通常都会以这种方式直接指定要连接的库的路径。
前面已经说明过了,库搜索路径的设置有两种方式:在环境变量LD_LIBRARY_PATH 中设置以及在/etc/ld.so.conf 文件中设置。其中,第二种设置方式需要root 权限,以改变/etc/ld.so.conf 文件并执行/sbin/ldconfig 命令。而且,当系统重新启动后,所有的基于GTK2 的程序在运行时都将使用新安装的GTK+ 库。不幸的是,由于GTK+ 版本的改变,这有时会给应用程序带来兼容性的问题,造成某些程序运行不正常。为了避免出现上面的这些情况,在GTK+ 及其依赖库的安装过程中对于库的搜索路径的设置将采用第一种方式进行。这种设置方式不需要root 权限,设置也简单:
exportLDLIBRARYPATH=/opt/gtk/lib: LD_LIBRARY_PATH
可以用下面的命令查看LD_LIBRAY_PATH 的设置内容:
echo LD_LIBRARY_PATH
至此,库的两种设置就完成了。
Retrieved from”http://www.microsuncn.com/index.PHP?title=%E8%AF%A6%E8%A7%A3pkg-config_–cflags_–libs_glib-2.0%E7%9A%84%E4%BD%9C%E7%94%A8”