一、GCC
GCC 编译器是 Linux 下默认的 C/C++ 编译器,大部分 Linux 发行版中都是默认安装的。GCC 编译器通常以 Linux 命令的形式在终端(Shell)中使用,它有很多选项,这是我们要重点学习的。
1.1 gcc的编译过程
- 预处理(pre-processing)E:插入头文件,替换宏
- 编译(Compiling)S:编译成汇编
- 汇编(Assembling) c:编译成目标文件
- 链接 (Linking):链接到库中,变成可执行文件
生成可执行文件
gcc -E hello.c -o hello.i
gcc -S hello.i -o hello.s
gcc -c hello.s -o hello.o
#链接,生成可执行文件
gcc hello.s -o hello
# 运行
/hello
一次性生成可执行文件
# 也可以一次性完成
# 但一般情况下生成.o文件比较好,可以重定位文件,让别人使用
gcc hello.c -o hello
1.2 gcc常用选项
选项 | 描述 |
---|---|
-o | 生成目标 |
-c | 取消链接步骤,编译源码并最后生成目标文件 |
-E | 只运行C预编译器(头文件,宏等展开) |
-S | 生成汇编语言文件后停止编译(.s文件) |
-Wall | 打开编译告警(所有) |
-g | 嵌入调试信息,方便gdb调试 |
-l<lib> | 指定程序要链接的库(这里是小写 L ) 如果gcc编译选项中加入了“-static”表示寻找静态库文件 |
-L<Dir> | 指定-l(小写-L)所使用到的库文件所在的目录名,【增加 lib 目录 (静态库和动态库)】 |
-I<dir> | 增加 include 目录 (这里是大写 i ) 头文件路径 |
-D<define> | 预定义宏 |
-O | 优化参数,一般用-O2就是,用来优化程序用的,比如gcc test.c -O2,优化得到的程序比没优化的要小,执行速度可能也有所提 |
-shared | 指定命令编译动态库 |
-fPIC | 作用于编译阶段,告诉编译器产生与位置无关代码(Position-Independent Code);这样一来,产生的代码中就没有绝对地址了,全部使用相对地址,所以代码可以被加载器加载到内存的任意位置,都可以正确的执行。这正是共享库所要求的,共享库被加载时,在内存的位置不是固定的。 |
1.3 多模块编译
一次性编译:
gcc -Wall fun.c main_fun.c -o main_fun
独立编译:
gcc -Wall –c main_fun.c -o main_fun.o
gcc -Wall -c fun.c -o fun.o
gcc -Wall main_fun.o fun.o -o main_fun
多模块编译中如果某一个模块发生了变化,只需要编译更改的模块即可
1.4 静态库(.a)
程序在编译链接时候把库的代码链接到可执行文件中。程序运行时候,不再需要静态库,生成的可执行文件大,每个可执行文件都会加载一份拷贝到内存。
静态库生成(libxxx.a)
# 首先生成.o文件,然后通过.o文件生成.a文件
gcc -c fun.c
ar rcs libfun.a fun.o
静态库使用
gcc -Wall main.c libfun.a -o main
# -L.表示在当前目录搜索 libfun.a
gcc -Wall -L. main.c -o main -lfun
静态库搜索路径
- 1:编译使用选项 -L 指定的目录(建议)
- 2:修改环境变量,搜索指定的目录 设置环境变量 LIBRARY_PATH
export LIBRARY_PATH=“库目录” - 3:lib文件放入系统指定目录,例如/usr/lib/等
1.5 共享库(.so或.sa)
程序运行时候才去链接共享库代码,多个程序共享使用,使用时候只需要加载一份到内存
1)共享库生成
gcc -shared -fPIC fun.c -o libFun.so
- shared:生成共享库格式
- fPIC: 产生位置无关码,允许在任何地址加载 (否则只能从指定地址加载,无法控制)相对地址
2)共享库使用
gcc –Wall main.o –o main –L. -lFun
只需要-l+文件名即可
配置方式
- 1:拷贝到so文件到共享库目录 /usr/lib/
- 2:更改
LD_LIBRARY_PATH
,如果已经有了一个这样的目录,要写成export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:“库目录”
这样子。如果写在profile配置文件里export LD_LIBRARY_PA TH="目录:$LD_LIBRARY_PATH"
- 3:配置
/etc/ld.so.conf
; 并使用ldconfig
命令进行更新
二、参数详解
2.1 gcc -l
-l
参数就是用来指定程序要链接的库,-l
参数紧接着就是库名,那么库名跟真正的库文件名有什么关系呢?就拿数学库来说他的库名是m
,他的库文件名是libm.so
,很容易看出,把库文件名的头lib
和尾.so
去掉就是库名了。好了现在我们知道怎么得库名,当我们自已要用到一个第三方提供的库名字libtest.so
,那么我们只要把libtest.so
拷贝到/usr/lib
里,编译时加上-ltest
数,我们就能用上libtest.so
库了(当然要用libtest.so
库里的函数,我们还需要与libtest.so
配套的头文件)。
2.2 gcc -L
放在
/lib
和/usr/lib
和/usr/local/lib
里的库直接用 -l 参数就能链接了,但如果库文件没放在这三个目录里,而是放在其他目录里,这时我们只用-l
参数的话,链接还是会出错,出错信息大概是:“/usr/bin/ld: cannot find -lxxx”
,也就是链接程序 ld 在那3个目录里找不到libxxx.so
。这时另外一个参数-L
就派上用场了,比如常用的X11
的库,它在/usr/X11R6/lib
目录下,我们编译时就要用-L/usr/X11R6/lib -lX11
参数,-L
参数跟着的是库文件所在的目录名。再比如我们把libtest.so
放在/aaa/bbb/ccc
目录下,那链接参数就是-L/aaa/bbb/ccc -ltest
。
2.3 gcc -I
-I
参数是用来指定头文件目录,/usr/include
目录一般是不用指定的,gcc
知道去那里找,但是如果头文件不在/usr/include
里我们就要用-I
参数指定了,比如头文件放在/myinclude
目录里,那编译命令行就要加上-I/myinclude
参数了,如果不加你会得到一个"xxxx.h: No such file or directory"
的错误。-I
参数可以用相对路径,比如头文件在当前目录,可以用-I.
来指定。