GCC编译四个步骤
预处理
预处理环节主要是进行头文件、宏的展开
例如我们有一个简单的hello.c
程序:
1 |
|
我们可以使用-E
选项让gcc只运行C预编译器
1 | gcc -E hello.c -o hello.i |
查看hello.i
文件可以看到,我们的HELLOWORLD
宏已经被展开了
编译
可以使用-S
选项告诉编译器产生汇编语言文件后停止编译,产生的汇编语言文件扩展名为.s
gcc -S hello.i -o hello.s
查看hello.s
文件可以看到,生成了汇编文件
汇编
gcc会调用as工具生成可重定位目标文件
可以使用 -c
选项通知gcc取消链接步骤,即编译源码并在最后生成目标文件
gcc -c hello.s -o hello.o
链接
gcc hello.o -o hello
gcc hello.c -o hello
直接编译链接成可执行目标文件
gcc -c hello.c
或 gcc -c hello.c -o hello.o
编译生成可重定位目标文件
gcc实用选项
-Wall 选项
我们有一个 bad.c
程序
编译 gcc bad.c -o bad
运行 ./bad
,发现输出 0.000000
,与我们预期输出 4
不符,原因是应该输出 %d
但是使用了%f
1 |
|
如果使用 -Wall
选项可以检查出错误
gcc -Wall bad.c -o bad
gcc编译多文件
我们现在有三个文件,分别是 hello_fn.c
、hello_fn.h
、main.c
1 | // hello_fn.h |
1 | // hello_fn.c |
1 | // main.c |
一次性编译
1 | gcc -Wall hello_fn.c main.c -o newhello |
独立编译
独立编译的好处是,如果只有某个模块改变了,只需要编译这个模块,而不需要所有的模块全部编译
1 | gcc -Wall -c main.c -o main.o |
使用外部库
静态库与动态库
- 静态库( .a ):程序在编译链接的时候把库的代码链接到可执行文件中。程序运行的时候将不再需要静态库。
- 共享库( .so或.sa ):程序在运行的时候才去链接共享库的代码,多个程序共享使用库的代码。
- 一个与共享库链接的可执行文件仅仅包含它用到的函数入口地址的一个表,而不是外部函数所在目标文件的整个机器码
- 在可执行文件开始运行以前,外部函数的机器码由操作系统从磁盘上的该共享库中复制到内存中,这个过程称为动态链接( dynamic linking )
- 共享库可以在多个程序间共享,所以动态链接使得可执行文件更小,节省了磁盘空间。操作系统采用虚拟内存机制允许物理内存中的一份共享库被要用到该库的所有进程共用,节省了内存和磁盘空间
生成静态库
假设之前的 hello_fn.h
、hello_fn.c
、main.c
三个文件
1 | gcc -Wall -c hello_fn.c -o hello_fn.o |
ar
是 gnu
归档工具,rcs
表示( replace and create )
1 | gcc -Wall main.c libhello.a -o main |
另外一种连接方式,-l{库名}
,lib
和.a
都需要省略。-L.
表示库文件在当前目录下
1 | gcc -Wall -L. main.c -o main -lhello |
生成共享库
示例:gcc -shared -fPIC hello_fn.o -o libhello.so
- shared:表示生成共享库格式
- fPIC: 产生位置无关码(position independent code)
- 库名规则:libxxx.so
运行共享库
- 拷贝
.so
文件到系统共享库路径下,一般指/usr/lib
,然后运行ldconfig
刷新库缓存 - 更改 LD_LIBRARY_PATH
- ldconfig 配置 ld.so.conf,ldconfig 更新 ld.so.cache