参考GCC
源代码到可执行文件的步骤
- 预处理:主要是处理那些源文件和头文件中以
#
开头的命令(后缀.i
) - 编译:把预处理完的文件进行一系列分析和优化后,生成汇编代码文件(后缀
.s
) - 汇编:将汇编代码转换成可执行的机器指令(目标文件)(后缀
.o
) - 链接:目标文件已经是二进制文件了,与可执行文件的组织形式类似,只是有些函数和全局变量的地址还没找到,程序不能执行。链接就是找到目标地址,将所有的目标文件组织成一个可执行文件(后缀
.out
)
GCC(编译器)
-
查看
gcc
的版本gcc --version
-
编译源代码(一步完成)
gcc demo.c -o demo # 将源代码 demo.c 直接编译生成可执行文件 demo # -o选项是 生成文件的文件名
-
64
位系统下,按照32
位环境编译gcc demo.c -o demo -m32
-
gcc/g++指令选项 功能 -E 预处理指定的源文件,不进行编译 -S 编译指定的源文件,但是不进行汇编 -c 编译、汇编指定的源文件,但是不进行链接 -o 指定生成文件的文件名 -llibrary 其中 library 表示要搜索的库文件的名称。该选项用于手动指定链接环节中程序可以调用的库文件 -ansi 对于C语言程序来说,其等价于 -std=c90;对于C++程序来说,其等价于 -std=c++98 -std= 手动指定编程语言所遵循的标准 -
-l
选项:手动添加链接库链接库是一个具有许多目标文件的集合,它们在一个文件中以方便处理
gcc demo.c -o demo -lm # 举例:数学库的文件名是 libm.a # 前缀 lib 和 后缀 .a 是标准的,m 是基本名称 # -l选项后紧跟着的是库的基本名称
-
一次处理多个文件
# 假设有两个源代码 demo1.c demo2.c,可以一起编译 gcc demo1.c demo2.c -o main # 调用的时候 一定要有头文件 # 或者说有更多的源代码 要一起编译 gcc *.c -o main
-
使用静态链接和动态链接
注意: 动态链接和静态链接相比,静态链接生成的可执行文件的体积更大
- 库文件:包含有功能实用的目标文件
- 静态链接库:采用静态链接方式实现链接操作的库文件
- 在程序文件中哪里用到了库文件中的功能模块,GCC编译器就会将该模板代码直接复制到程序文件的适当位置,最终生成可执行文件。
- 优点:生成的可执行文件不再需要任何静态库文件的支持,就可以独立运行(可移植性强)
- 缺点:如果程序文件中多次调用库中的同一功能模块,则该模块代码就会被复制多次,生成的可执行文件会包含多段完全相同的代码,造成代码的冗余
- 动态链接库:采用动态链接方式实现链接操作的库文件
- 程序文件中哪里需要库文件,GCC编译器 不会 直接把该功能模块的代码拷贝到文件中,而是将功能模块的位置信息记录到文件中,直接生成可执行文件
- 优点:由于可执行文件中记录的是 功能模块的地址,真正的实现代码会在程序运行时被载入内存,即便功能模块被调用多次,使用的都是同一份代码(动态链接库又被称为共享链接库)
- 缺点:生成的可执行文件无法独立运行,必须借助响应的库文件(可移植性差)
-
静态链接库
-
我们实现通过源代码实现了一个功能(包含很多源代码),但是我们仅希望别人使用我们实现的功能,不想它们看到具体实现的源码,很简单,就是把它们加工一个 静态链接库
-
静态链接库的创建
源文件加工厂静态链接库,满足以下2个条件:
1、源文件中只提供可以重复使用的代码,例如函数、设计好的类等,不包含main函数
2、源文件在实现具备模块功能的同时,还要提供访问它的接口,也就是包含各个功能模块声明部分的头文件
# 假设有 add.c、sub.c 和 div.c # 1、将所有的源文件,编译成相应的目标文件: gcc -c add.c sub.c div.c # 2、使用 ar 压缩命令,将生成的目标文件打包成静态链接库 ar rcs 静态链接库名称 目标文件1 目标文件2 .... # 静态链接库的命名规则:libxxx.a --- xxx可以自己定义 # Linux 系统下,静态链接库的后缀名为 .a # Windows 系统下,静态链接库的后缀名为 .lib
-
静态链接库的使用
就是在程序的链接阶段,将静态链接库和其它目标文件一起执行链接操作,从而生成可执行文件
# 1、先将 main.c 文件编译成目标文件 gcc -c main.c # 2、链接操作 gcc -static main.o libxxx.a -o main # 其中 -static 选项强制GCC编译器使用静态链接库 # 3、如果GCC编译器提示无法找到 静态编译库,则使用下面的链接操作 gcc main.o -static -L /root/demo/ -lxxxx -o main # -L 选项用于向GCC编译器指明静态链接库的存储位置 # -l 选项用于指明所需静态链接库的名称,xxx就是静态链接库的名称
-
-
动态链接库
GCC编译器生成可执行文件时,默认情况下会优先使用动态链接库实现链接操作,除非当前系统环境中没有程序文件所需要的动态链接库,GCC编译器才会选择相应的静态链接库。如果两种都没有,则链接失败。
-
动态链接库的创建
-
直接使用源文件创建动态链接库
# 假设有 add.c、sub.c 和 div.c gcc -fpic -shared add.c sub.c div.c -o libmymath.so # -shared选项用于生成动态链接库 # -fpic选项是,令GCC编译器生成动态链接库(多个目标文件的压缩包)时,表示各目标文件中函数、类等功能模块的地址使用 相对地址。这样,无论将来链接库被加载到内存的什么位置,都可以正常使用 # Linux 系统下,静态链接库的后缀名为 .so # Windows 系统下,静态链接库的后缀名为 .ddl
-
两步得到动态链接库
# 假设有 add.c、sub.c 和 div.c # 1、将源文件编译成目标文件 gcc -c -fpic add.c sub.c div.c # -fpic选项是,令GCC编译器生成动态链接库(多个目标文件的压缩包)时,表示各目标文件中函数、类等功能模块的地址使用 相对地址。这样,无论将来链接库被加载到内存的什么位置,都可以正常使用 # 2、利用目标文件,生成动态链接库 gcc -shared add.o sub.o div.o -o libmymath.so # -shared选项用于生成动态链接库 # Linux 系统下,静态链接库的后缀名为 .so # Windows 系统下,静态链接库的后缀名为 .ddl
-
-
动态链接库的使用
因为前面将
add.c
、sub.c
、div.c
打包到了libmymath.so
动态链接库中,此时该项目只剩下了main.c
源程序文件,因此执行此项目,就变成了将main.c
和libmymath.so
进行链接,生成可执行文件。注意:
test.h
头文件并不直接参与编译,因为在程序的预处理阶段,已经对项目中需要用到的头文件进行了处理。# 1、直接生成可执行文件 gcc main.c libmymath.so -o main # 生成的 main 通常无法直接执行,在执行过程中无法找到 libmymath.so 动态链接库。 # 2、通过 lld main 指令,可以查看当前文件在执行时需要用到的所有动态链接库,以及各个库文件的存储位置 lld main
运行由动态链接库生成的可执行文件时,必须确保程序在运行时,可以找到这个动态链接库,常用的解决方案有:
-
将链接库文件移动到标准库目录下
比如:
/usr/lib
、/usr/lib64
、/lib
、/lib64
-
在终端输入
export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:xxx
,其中xxx
为动态链接库的绝对存储路径(此方式仅在当前终端有效,关闭终端后无效) -
修改
~/.bashrc
或~/.bash_profile
文件,即在文件的最后一行添加export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:xxx
,其中xxx
为动态链接库的绝对存储路径。保存之后,执行source .bashrc
指令(此方式只对当前登录用户有效)
-
-
-
GCC找不到库文件怎么办
-
找不到静态链接库
# 1、第一种写法 gcc -static main.c libmymath.a -o main # 2、第二种写法 gcc -static main.c -lmymath -o main
当以第一种写法完成链接操作时,GCC编译器只会在当前目录中,查找
libmymath.a
静态链接库;反之,如果使用-l
选项(第二种写法)指明了要查找的静态库的文件名,则GCC编译器会按照如下顺序,依次到指定目录中查找所需的库文件:- 如果 gcc 指令使用了
-L
选项指定了查找路径,则GCC编译器会优先选择去该路径下查找所需要的库文件; - 再到 Linux 系统中
LIBRARY_PATH
环境变量指定的路径中搜索需要的库文件; - 最后再到GCC编译器默认的搜索路径(比如
/lib
、/lib64
、/usr/lib
、/usr/lib64
、/usr/local/lib
、/usr/local/lib64
)中查找
注意:
如果读者使用第一种方法完成链接操作,但GCC编译器提示找不到所需库文件,表明所用库文件并未存储在当前路径下,解决方案就是手动找到库文件并将其移至当前路径,然后重新执行链接操作。
反之,如果读者使用的是第二种方法,也遇到了GCC编译器提示未找到所需库文件,表明库文件的存储路径不对,解决方案有以下3种:
-
手动找到该库文件,并在 gcc指令中 用
-L
选项明确指明其存储路径。比如libmymath.a
静态库文件存储在/usr
目录下,则完成链接操作的 gcc指令为:gcc -static main.c -L /usr -lmymath -o main
-
将库文件的存储路径添加到
LIBRARY_PATH
环境变量中,在最后一行添加命令export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:xxx
,xxx表示库文件的存储路径 -
将库文件移动到GCC编译器默认的搜索路径中
- 如果 gcc 指令使用了
-
找不到动态链接库
运行由动态链接库生成的可执行文件时,必须确保程序在运行时,可以找到这个动态链接库,常用的解决方案有:
-
将链接库文件移动到标准库目录下
比如:
/usr/lib
、/usr/lib64
、/lib
、/lib64
-
在终端输入
export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:xxx
,其中xxx
为动态链接库的绝对存储路径(此方式仅在当前终端有效,关闭终端后无效) -
修改
~/.bashrc
或~/.bash_profile
文件,即在文件的最后一行添加export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:xxx
,其中xxx
为动态链接库的绝对存储路径。保存之后,执行source .bashrc
指令(此方式只对当前登录用户有效)
-
-