GCC 常用编译选项及相关工具

这篇文章是对 gcc/g++ 经常使用的编译选项和有关工具的一个简单记录,包括静态库和共享库的生成和使用,以便备忘。

1、编译选项

-E 只进行预处理,不进行编译和连接,其输出默认是到终端。(x.c => x.i , x.cpp => x.ii)
-S 编译后仅生成汇编语言文件(x.cpp , x.ii => x.s),不生成目标文件。
-c 编译后生成目标文件(x.cpp , x.ii , x.s => x.o),不连接程序。
-save-temps 保留所有中间过程生成的文件,即 x.ii , x.s , x.o 文件。
-o 输出到文件,指定输出文件名。
-Wall 显示编译过程中的所有警告信息。
-g 在可执行文件中加入标准调试信息,用于程序调试。
-On 进行编译优化,n为优化级别,范围是0~3,在gcc-4.8中还可以用g,即-Og,有利于调试。
-Idir 将目录dir添加到头(Include)文件搜索范围
-Ldir 将目录dir添加到库(Library)文件搜索范围
-lmylib 连接时使用共享库libmylib.so(如果没有,就使用静态库libmylib.a),一般被调用者在调用者之后。
-Dname[=var] 定义宏变量name[=var]

2、静态库

在C语言中函数的声明和实现是可以分离的,同样在C++语言中类的声明和实现也是可以分离的。所以可以把函数和类的声明放在头文件中,而把函数和类的实现分开放在cpp文件中,在其中用#include "..." 语句把有关的声明引入。这样就可以把函数和类的具体实现单独编译成库文件,使库文件和头文件同时发布,供其它程序调用。这样做可以隐藏算法代码、节省其它程序的编译时间、容易实现软件的模块化。

程序库有两种:静态库(x.a)和共享库(x.so)。前者实际就是多个目标文件(x.o)的归档压缩文件(这与Java中的jar文件有些类似,jar文件是多个x.class字节码的压缩归档)。使用静态库进行程序连接时,连接器会直接把静态库中的某个目标文件复制到输出的可执行程序文件,这无疑会增加可执行程序文件的大小。后者共享库和Windows里的动态链接库(x.dll)是等价的。使用共享库,程序的连接不是在编译的过程中进行的,即不需要代码的复制,而是在主程序执行过程中动态进行的,动态加载直接来自共享库文件中的代码。共享库文件不再是多个目标文件的简单归档,其中必然还包括了更多的描述性信息,以方便程序连接器的工作。

下面先说一下怎样生成和使用静态库。

生成静态库所使用的命令是 ar 。最基本的生成静态库的方式如下:

g++ -c x1.cpp x2.cpp ...   # 输出为 x1.o x2.o ...
ar crv libx.a x1.o x2.o ...   # 输出为 libx.a

关于命令 ar 的常用选项:

c 创建库文件。
r 在库文件中的最后插入目标文件,如果存在重名,替换掉。
d 从库文件中删除指定目标文件。
t 列出库文件中所有目标文件的文件名列表。
s 为库文件建立函数索引(或更新),利于连接时检索。
x 从库文件中提取某个或全部目标文件。
v 显示执行结果。

另外,如果在库文件搜索路径中同时存在静态库和共享库,gcc/g++ 会优先使用共享库。使用选项 -static 可以强制使用静态库。 -static 选项会强制所有的库都使用静态库,如果想只让特定的库使用静态库,可以引用该库的静态库的完整路径,如下:

g++ main.cpp /path/to/libx.a -ldynamic1 -ldynamic2 -o main

3、共享库

在计算机内部,共享库显然是最常用的。不论是在硬盘中还是在内存中,公共的代码只保留一份,而不是每个程序各自持有一份,这样大大节省了空间。

在编译用于生成共享库的目标文件时,必须加上选项 -fPIC ,即生成位置无关的目标代码(Position Independent Code)。具体生成方式如下:

g++ -c -fPIC x1.cpp x2.cpp ...   # 输出为 x1.o x2.o ...
g++ -shared -o libx.so x1.o x2.o ...   # 输出为 libx.so

也可以从源代码直接生成:

g++ -fPIC -shared -o libx.so x1.cpp x2.cpp ...   # 输出为 libx.so

要调用共享库里的函数,是需共享库的头文件的,其中包括了共享库中所有函数或类的声明。在调用共享库中的函数之前,要先引入共享库的头文件。编译时按如下方式:

g++ main.cpp -lx -o main   # 如有必要,要把该共享库文件所在文件夹加入 g++ 库文件搜索路径,如: -L.

要想查看一个程序依赖哪些共享库,可以使用 ldd 命令。

实际上共享库的中代码是与调用者主程序在运行时动态地连接的(由 ld.so 负责)。主程序在运行时,面临的问题是到哪去找所需的共享库文件。有一个固定的共享库搜索顺序,ld.so 是按照这个顺序进行搜索共享库文件的:

  1. 环境变量 LD_LIBRARY_PATH 的值指定的路径:
    如可以这样设置:export LD_LIBRARY_PATH=.:$LD_LIBRARY_PATH;
  2. 共享库文件索引缓存文件:/etc/ld.so.cache :
    在文件 /etc/ld.so.conf 或 /etc/ld.so.conf.d 中的 *.conf 文件中加入共享库文件所在的文件夹路径,然后执行 ldconfig 命令创建(或更新)库链接和缓存文件;
  3. 库文件默认目录:/lib , /usr/lib

需要注意的是,在 /etc/ld.so.conf.d 中的 *.conf 文件中,每个文件夹路径占一行,且在执行 ldconfig 创建或更新缓存文件时是不包括子目录的,所以当子目录中也有所需的库文件时,也要把子目录加入到 /etc/ld.so.conf.d/*.conf 中。

还有一点:主程序在编译和运行时使用的是相同的共享库文件,只是在程序编译时需要库文件的头文件,运行时是不需要的。

[完]

Leave a Reply

Your email address will not be published. Required fields are marked *

*