Linux C gcc的使用
gcc的使用
更多信息见gcc
编译过程
# 预处理:demo.c -> demo.i
gcc -E demo.c -o demo.i
# 编译:编译成汇编语言 demo.i -> demo.s
gcc -S demo.i -o demo.s
# 汇编:使用汇编器翻译成机器语言 demo.s -> demo.o
gcc -c demo.s -o demo.o # demo.o是可重定位目标文件
# 链接:demo.o [*.a] [*.so] -> demo.out
ld demo.o *.a -o demo.out
编译过程的路径处理
#include
的路径搜索的顺序:
- 如果源代码中没有指明头文件的路径,如
#include "mylib.h"
,那么必须在编译和其他对源代码处理的环节都需要指明头文件的位置,方式是使用-I dir
指明头文件所在的路径,通常一个库的头文件放置在include
目录下,即使用-I xxx/include
; - 如果源代码中指明头文件的绝对或相对路径,如
#include "/xxx/xxx/mylib.h"
或#include "../mylib.h"
,则不需要在后续处理中指明,但是在源代码中指明的方式增加的工程的复杂性和文件间的依赖性(这种依赖性可以通过cmake工具简化)。
- 首先搜索编译时的工作路径,即
.
iquote dir
提供的路径,c文件中的形式只能是#include "file.h"
的引用形式,其他三种命令也支持使用#include<file.h>
。-I dir
提供的路径-isystem dir
提供的路径- 标准库的路径
-idirafter dir
提供的路径
库文件的创建和使用
静态库的创建
静态库必须要求以lib
开头
# 制作静态库
ar rcs libxxx.a xxx.o yyy.o ..
# 查看.a文件中的函数
ar -t *.a
# 查看.a文件的反汇编代码
objdump -S libxxx.a
ar
命令的解释:一个打包工具,将成员文件按照一定的规则构建到.a文件中r
替换归档文件中已有的文件或加入新文件c
没有库文件时创建s
作为ranlib
工作为库中的可重定位目标文件创建符号索引或者更新
动态库的创建
# 制作动态库
gcc -shared -o libxxx.so xxx.o yyy.o ..
# 查看.so文件的反汇编代码
objdump -S libxxx.so
-
-shared
产生一个共享对象(该对象具有执行权限),然后可以将其与其他对象链接以形成可执行文件。-shared- 在生成动态库文件时必须指明这一操作,向gcc声明生成的文件是动态可链接的,否则在链接时会出现
/usr/bin/ld: /usr/lib/gcc/x86_64-linux-gnu/9/../../../x86_64-linux-gnu/Scrt1.o: in function `_start':
(.text+0x24): undefined reference to `main'
collect2: error: ld returned 1 exit status
- 对使用
-shared
指令处理的文件进行执行时会出现段错误 (核心已转储)
-fPIC
指明可重定位目标文件与位置无关,一般gcc默认开启
库文件的使用
注意对顺序的要求
-static
强制使用静态库,如果库文件目录下有同名的静态库和动态库,gcc默认使用动态库,否则需要使用-static
强制使用静态库-L dir
指明库文件的路径-l xxx
或-lxxx
指明需要链接的库的名称,其中库文件的名称是libxxx.a
或libxxx.so
- 使用动态库时如果库不在当前工作目录下,则可能找不到,应该设置环境变量
LD_LIBRARY_PATH=./lib/
注意
-shared
和-static
并不是两个相对的指令或概念,-shared
表示在制作动态库时,向gcc
声明生成的文件是动态可链接的,以供其他对象链接,-static
表示在生成可执行文件时强制使用静态库文件。如果在生成可执行文件中添加-shared
,表示该“可执行文件”可以被其他文件链接,这显然造成错误(尝试执行会出现段错误)
信息的查看
objdump
案例
项目的原始结构
.
├── bin
├── build
│ └── CMakelists.txt
├── include
│ ├── addvec.h
│ ├── mulvec.h
│ └── print.h
├── lib
├── out
└── src
├── addvec.c
├── main.c
├── mulvec.c
└── print.c
main源代码使用addvec、mulvec和print文件的函数,这些函数的声明在对应的头文件下。main中的include的写法为
#include "addvec.h"
#include "mulvec.h"
#include "print.h"
编译生成可重定位目标文件
# 注意 我的gcc默认使用-fPIC进行编译,所以这里加不加都可
gcc -fPIC -o ./out/addvec.o -c ./src/addvec.c -I ./include/
gcc -fPIC -o ./out/mulvec.o -c ./src/mulvec.c -I ./include/
gcc -fPIC -o ./out/print.o -c ./src/print.c -I ./include/
gcc -fPIC -o ./out/main.o -c ./src/main.c -I ./include/
制作库
静态库
ar rcs ./lib/libmath.a ./out/addvec.o ./out/mulvec.o
ar rcs ./lib/libprint.a ./out/print.o
动态库
gcc -shared -o ./lib/libmath.so ./out/addvec.o ./out/mulvec.o
gcc -shared -o ./lib/libprint.so ./out/print.o
链接生成可执行文件
# 使用静态库和使用静态库生成的文件
gcc -static -o ./bin/prog_static ./out/main.o -L ./lib/ -lprint -lmath
./bin/prog_static
# 使用动态库和使用动态库生成的文件
gcc -o ./bin/prog_shared ./out/main.o -L ./lib/ -lprint -lmath
LD_LIBRARY_PATH=./lib/ ./bin/prog_shared
编译后结构
.
├── bin
│ ├── prog_shared
│ └── prog_static
├── build
│ └── CMakelists.txt
├── include
│ ├── addvec.h
│ ├── mulvec.h
│ └── print.h
├── lib
│ ├── libmath.a
│ ├── libmath.so
│ ├── libprint.a
│ └── libprint.so
├── out
│ ├── addvec.o
│ ├── main.o
│ ├── mulvec.o
│ └── print.o
└── src
├── addvec.c
├── main.c
├── mulvec.c
└── print.c