今天我们主要学习gcc的一些东西
一. gcc的四步骤
我们都知道,可以使用gcc命令将一个.c文件编译成一个可执行文件,其实中间是有四个步骤的,我们可以来了解一下
1. 预处理
预处理会将.c文件变成一个.i文件,这个步骤的目的是将.c文件里面的头文件以及各种宏定义找到
2. 编译
编译是将.i文件变成一个.s文件,这个步骤的是将我们的源码编译成汇编码
3. 汇编
汇编是将.s文件变成.o文件,这个步骤是将我们的汇编码变成机器码
4. 链接
链接是将各个文件链接起来,组成一个可执行文件
二. gcc的选项
我们一般会直接使用gcc -o a b.c这条命令,将b.c文件编译成一个可执行文件,但是其实gcc这个命令有很多参数可供我们选择,我们接下来看一些常用的选项
-E : 预处理
-c : 把预处理,编译,汇编都做了,但是不做链接
-o : 指定输出文件
-I : 指定头文件目录
-L : 指定链接是库文件目录
-l : 指定链接哪一个库文件(L的小写,这里显示貌似有问题)
我接下来会用比较多的例子来让大家理解这几个选项
main.c
sub.h
sub.c
上面三个是我已经写好的文件,我用这三个文件来告诉大家gcc的选项如何使用
1. 编译多个文件
大家可以看到,在这里我们使用gcc命令将main.c文件编译成可执行文件失败了,原因是什么呢??因为我们在main.c中使用了sub_fun这个函数,而这个函数定义在sub.h中,实现在sub.c中,我们需要把这个sub文件链接进来,这样子才不会出现问题。这里有两种方式,你可以将main.c和sub.c两个文件一起编译、链接,也可以分开编译,最后统一链接,总之最后要链接在一起这个是核心的东西!!
1.1 一起编译、链接
gcc -o test main.c sub.c
因为gcc如果不加其他参数就会默认把四步骤直接做完,这里在最后将main和sub统一链接了,我们无需操心其中的细节
1.2 分开编译,统一链接
// 将main.c预处理、编译、汇编成.o文件
gcc -c -o main.o main.c
//将sub.c预处理、编译、汇编成.o文件
gcc -c -o sub.o sub.c
// 将两个.o文件链接在一起
gcc -o test main.o sub.o
2. 头文件(include)
大家可以看到,我将main.c文件中的头文件的sub.h从之前的"sub.h"改成了现在的<sub.h>,然后去编译就出现了问题,它说找不到sub.h这个文件,这是为什么呢?? 因为<>和""包含头文件系统去寻找的地方不是一样的
<> : 如果是用这个包含头文件,系统会去系统目录中寻找,只需什么是系统目录这个不需要了解太多
"" : 如果是用这个包含的头文件,系统会去当前目录下寻找
上述中,sub.h是我自己写的头文件,没有加入到系统目录中,所以如果使用<>包含sub.h的话,自然而然是找不到的。这个时候我们就可以使用-I选项了
我们使用了-I ./这个选项,就是告诉系统,首先在-I后面所指定的目录寻找,如果没有,再去系统目录找。我们的sub.h不就在当前目录嘛,这个自然而然就可以找到了。
solution
如果以后需要找不到头文件,理论上是有两种解决方案的。第一,将头文件添加到系统目录中。第二,使用-I选项来指定头文件的路径
3. 静态库,动态库
大家可以看到如果gcc只操作main.c就会出现这个错误,这个错误的意思是sub_fun这个函数没有定义就使用了,这个是不允许的。那那么我们如何解决这个问题呢??答案就是使用库
3.1 静态库
gcc -c -o main.o main.c
gcc -c -o sub.o sub.c
// 生成静态库
ar crs libsub.a sub.o
链接main.o和静态库
gcc -o test main.o libsub.a
这里我们将sub.o文件生成了一个名字为libsub.a的静态库,我们在最后链接的时候将main.o和静态库一起链接就好了。生成静态库的那个命令ar crs是命令,libsub.a是静态库的名字。
注意 我们可以将多个.o文件生成一个静态库,比如说我们在main.c中调用了很多其他文件里面的函数,但是不想最后将它们链接在一起,这个时候可以将其他文件生成一个静态库,然后将main.c和静态库一起链接即可,命令如下
ar crs libsub.a sub.o sub1.o sub2.o
还有就是在最后链接那个静态库和main.c的时候,如果静态库不在当前目录下,我们要记得指定静态库的绝对路径,这样系统才能找得到
3.2 动态库
ggc -c -o main.o main.c
gcc -c -o sub.o sub.c
// 生成动态库
gcc -shared -o libsub.so sub.o
// 链接main.c和动态库
gcc -o test main.o libsub.so
这个和静态库貌似看起来只有生成动态库那里有差别,我们要讲肯定要讲点不一样的东西
我们常用这一个命令
gcc -o test main.o -lsub
和上一条命令是一样的,但是-l选项是指定我们要去链接哪个库,我们在上面生成了libsub.so这个动态库,我们这里只需要-lsub即可,lib和后缀都可以省略
但是这里又又又又出问题了,它说找不到动态库,为什么呢???这里其实就是类似于头文件找不到问题了,它去找动态库的时候会去系统目录里面找,但是刚才那个动态库是我们自己生成的呀,在当前目录下,肯定是找不到的啦,解决方法和头文件一样。第一将自己生成的动态库加入到系统目录中。第二,使用-I来指定动态库的路径
gcc -o test main.o -L ./ -lsub
3.3 运行
大家会发现,使用了动态库和main.c链接是成功的,但是最后运行时有问题的,这个因为链接是链接,运行是运行,链接指定了动态库的路径,人家系统知道去哪里找那玩意,可是你运行的时候也没告诉人家去哪里找是吧。这个时候大家需要添加一下路径。
export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:./
这个命令首先是去原来的路径寻找,发现没有,那么就来我们配置的路径里面去找。