1 gcc编译流程
gcc编译程序主要经过四个过程:
- 预处理(Pre-Processing)
- 编译 (Compiling)
- 汇编 (Assembling)
- 链接 (Linking)
(1)预处理
在预处理阶段,编译器主要作加载头文件、宏替换、条件编译的作用。一般处理带“#”的语句。
我们可以通过gcc 的 -E 选项进行查看,如下所示:
gcc -E main.c -o main.i
编译器将main.c预处理结果输出 main.i 文件。
(2)编译
在编译过程中,编译器主要作语法检查和词法分析。在确认所有指令都符合语法规则之后,将其翻译成等价的中间代码或者是汇编代码。
gcc -S main.i -o main.s
编译器将预处理结果文件main.i翻译成汇编代码main.s
(3)汇编
汇编阶段是把编译阶段生成的”.s”文件转成二进制目标代码。
gcc -c main.s -o main.o
编译器将main.s文件转化为main.o 文件。
(4)链接
在成功编译之后,就进入了链接阶段。链接就是将目标文件、启动代码、库文件链接成可执行文件的过程,这个文件可被加载或拷贝到存储器执行。
gcc main.o -o main.exe
编译器将main.o链接成最终可执行文件main.exe
2 gcc常用选项
选项名 | 作用 | 生成 |
---|---|---|
-o | 产生目标 | .i、.s、.o、可执行文件等 |
-E | 只运行C预编译器 | .c文件 -> .i文件 |
-S | 告诉编译器产生汇编程序文件后停止编译 | .i文件 -> .s文件 |
-c | 通知gcc取消连接步骤,即编译源码,并在最后生成目标文件 | .s文件 -> .o文件 |
现在我们有源文件hello.c,下面是一些gcc的使用示例:
gcc -E hello.c -o hello.i 对hello.c文件进行预处理,生成了hello.i文件
gcc -S hello.i -o hello.s 对预处理文件进行编译,生成了汇编文件
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 编译生成可重定位目标文件
3 gcc编译多个程序
// hello.c
#include<stdio.h>
#include"hello.h"
void printHello()
{
printf("hello world!\n");
}
//main.c
#include<stdio.h>
#include"hello.h"
int main()
{
printHello();
return 0;
}
//hello.h
//头文件hello.h,仅包含函数声明
#ifndef _HELLO_
#define _HELLO_
void printHello();
#endif
编译这三个文件,可以一次编译生成可执行文件main
gcc hello.c main.c -o main
注意若不在同一目录下,需要用-I链接库文件路径(大写i)
在gcc 的-I参数后加上静态库头文件的路径。(大写i)
在gcc 的-L参数后加上库文件所在目录
在gcc 的-l参数后加上库文件名,不过要去掉lib和.a扩展名。(小写L)
也可以独立编译:
gcc -Wall -c main.c -o main.o
gcc -Wall -c hello.c -o hello.o
gcc -Wall main.o hello.o -o main
-Wall:使gcc对源文件的代码有问题的地方发出警告
4 生成静态库
静态库就是一些.o目标文件的集合,以.a结尾
为了生成.a文件,我们需要先生成.o文件。下面这行命令将我们的hello.o打包成静态库libhello.a:
ar rcs libhello.a hello.o
注: rcs中的r表明将模块hello.o加入到静态库中,c表示创建静态库,s表示生产索引。
ar是gun归档工具,如果libhello之前存在,将创建新的libhello.a并将其替换。
然后就可以这样来使用静态库libhello.a
gcc -Wall main.c libhello.a -o main
./main
-L指定路径,-l指定具体库,配合使用
本例中-L.指示链接库路径(当前路径),-l指定链接库。库名为hello(不指定前缀lib和后缀.a)
-I参数用来指定头文件目录,用来指定程序要链接的库(库文件在/lib、/usr/lib和/usr/local/lib下)。-l参数紧接着就是库名,那么库名跟真正的库文件名有什么关系呢?就拿数学库来说,他的库名是m,他的库文件名是libm.so,很容易看出,把库文件名的头lib和尾.so去掉就是库名了。好了现在我们知道怎么得到库名了,比如我们自已要用到一个第三方提供的库名字叫libtest.so,那么我们只要把libtest.so拷贝到/usr/lib里,编译时加上-ltest参数,我们就能用上libtest.so库了
5 生成动态库
(1)理解动态库
在Linux系统下,创建动态链接库是件很简单的事情。只要在编译函数库源程序时加上-shared选项即可,这样所生成的执行程序即为动态链接库。从某种意义上来说,动态链接库也是一种执行程序。按一般规则,程序名应带.so后缀。
gcc -shared -fPIC -o libhello.so hello.c
参考博客:动态库基本原理和使用方法,-fPIC选项的来龙去脉 理解
(2)创建和使用动态链接库
gcc -c -fPIC hello.c
gcc -shared -o libhello.so hello.o
gcc main.c -L. -lhello
export LD_LIBRARY_PATH=./
./a.out