GCC进阶
说明
简单,但清晰的GCC进阶教程。
GCC编译一个C / C++程序有4个步骤:
- 预编译
- 编译
- 装载器
- 链接器
GCC提供选项来查看详细的编译过程。
详细模式
可以通过 -v (verbose) 选项查看详细的编译过程
> gcc -v hello.c -o hello.exe
定义宏
你可以使用-Dname 选项来定义一个宏,或者-Dname=value 定义一个值value的宏。如果该值包含空格,则应包含在双引号中。
头文件 (.h), 静态库(.lib, .a) 动态库 (.dll, .so)
静态库VS动态库
库是一个预编译的对象文件的集合,可以通过链接器链接到你的程序中。例如,前面使用的系统函数printf()。
有两种类型的外部库:静态库和动态库。
在不同的操作系统下,它们有不同的拓展名。
操作系统 | 静态库文件扩展名 | 动态库文件扩展名 |
---|---|---|
Unixes | “.a” (archive file) | “.so” (shared objects) |
Windows | “.lib” (library) | “.dll” (dynamic link library) |
它们的主要区别如下:
当你的程序与静态库链接时,程序中使用的外部函数的机器代码将被复制到可执行文件中。一个静态库可以通过存档程序”ar.exe”创建。
当你的程序与一个动态库链接时,只有在可执行文件中创建了一个小表。在可执行文件开始运行之前,操作系统加载所需的外部函数 - a 的机器代码,这被称为动态链接的过程。因为一个库的一个副本可以在多个程序之间共享,所以动态链接使可执行文件更小,并节省磁盘空间。此外,大多数操作系统允许在内存中的共享库的一个副本被所有的运行程序使用,因此,也节省了内存。动态库的代码可以升级而不需要重新编译你的程序。
从上面可以看到动态链接库的优点非常多,所以GCC默认是链接动态库的。
你可以通过 “nm filename”列出一个库的内容。
搜索头文件和库
编译程序时,编译器需要头文件来编译源代码;链接器需要库来解析来自其他对象文件或库的外部引用。你需要设置了适当的选项,告诉编译器和链接器来找到头文件和库,但是,这对于第一次使用的GCC的用户感觉有点迷糊。
每一个在源文件里使用的头文件(#include <头文件名字>),编译器 搜索所谓的 include-paths 来找头文件。include-paths是通过 -Idir 选项指定(大写字母“I”后面跟着的是目录路径,注意之间并没有空格),或者是环境变量 CPATH指定。由于头文件的文件名是已知的,所以只需要路径名就可以。
链接器 搜索所谓的 library-paths ,将程序链接到可执行文件所需的库。library-paths是通过 -Ldir 选项指定(大写字母“L”后面紧紧跟着的是目录路径,注意之间并没有空格
)(或环境变量LIBRARY_PATH
)。此外,还必须指定库名称,这里和编译器找头文件不同,头文件在源代码就有声明,所以不需要额外再去指定,而库需要使用 -l 选项来告诉链接器。
-lxxx 选项,小写字母“l”,在Unixes中,后面xxx没有后缀“.lib”和“.a”扩展名。在windows中,则提供完整的名称如-lxxx.lib。
默认的Include-paths, Library-paths以及库
列出默认情况下包括在您的系统中的Include-paths,使用GNU C预处理器的命令
cpp -v
在ubuntu系统下,会有类似下面信息显示:
#include "..." search starts here:
#include <...> search starts here:
/usr/lib/gcc/x86_64-linux-gnu/4.8/include
/usr/local/include
/usr/lib/gcc/x86_64-linux-gnu/4.8/include-fixed
/usr/include/x86_64-linux-gnu
/usr/include
这样,就会明白为什么在使用开发 C++和C的IDE工具(例如Visual studio),需要设置包括路径、库路径和库名字的意义了。
GCC环境变量
GCC使用下列环境变量:
PATH: 搜索可执行文件和运行时动态库 (.dll, .so)
CPATH: 为头文件搜索include-paths。在-IDir选项指定的路径之后,它将会被搜索。C_INCLUDE_PATH 和 CPLUS_INCLUDE_PATH 分别可用于指定C和C++头文件。
LIBRARY_PATH: 为链接库搜索library-paths。在-LDir选项指定的路径之后,它将会被搜索。
小结
- 通过 -v (verbose) 选项查看详细的编译过程。
- 动态链接库节省内存和硬盘空间,优先使用动态库。
- 编译器,搜索头文件,只需要指定路径,因为源文件给出了头文件名称,使用选项“-IDir”指定头文件所在路径。
- 链接器,搜索库文件,需要指定路径和库名称。因此,需要指定两个选项:“-LDir”,“-lDir”
- 在Unixes中,指定库名字没有后缀“.lib”和“.a”扩展名。在windows中,则提供完整的名称如-lxxx.lib。