编译型语言生成可执行文件的过程包括编译和链接。编译包括预处理、编译和汇编三个步骤。而链接则根据发生时间不同分为静态链接和动态链接。那么让我们来看看整个过程具体是如何执行的,这里以Linux环境下的c/c++语言为例。
上面提到链接分为静态链接和动态链接,静态链接先于动态链接出现,动态链接属于计算机发展较后期出现的技术。两者没有优劣之分,只是各自有各自适用场景。
实际开发过程中,将所有代码都放在一个源文件中对维护和管理不是很友好,因此会有多个源文件存在,且不同的源文件之间相互有依赖关系。如,不同源文件之间进行函数调用。
但在编译时期每个源文件都是单独编译,并生成各自对应的 *.o 文件,所以还需要有个将这些目标文件合并在一起生成一个可执行文件的过程。而这个过程就是静态链接(动态链接出现之后对链接的叫法)。
由多目标文件链接形成静态库,反之静态库也可以简单看成是一组目标文件的集合。
链接器在进行链接时以目标文件为单位。如果同一目标文件中包括很多函数,当我们仅需要调用其中单个函数时也会连同其他函数一起链接过来,造成内存空间的浪费,因此应尽量一个源文件中只包含一个函数。
制作链接库的目的是希望他人在使用我们的功能实现的同时不会拿到源代码,这种机制尤其对 商用软件非常友好。
Linux 中静态链接库文件命名规则为:libxxx.a,其中 xxx 代表库的名称,lib为固定前缀, .a 为固定后缀,如 libc.a。前缀和后缀在链接过程中由链接器自动追加。
ar rcs libxxx.a 目标文件列表
ar 是 Linux 的一个备份压缩命令,它可以将多个文件打包成一个备份文件(也叫归档文件),也可以从备份文件中提取成员文件。ar 命令最常见的用法是将目标文件打包为静态链接库。对参数的说明: r 用来替换库中已有的目标文件,或者加入新的目标文件。 c 表示创建一个库。不管库否存在,都将创建。 s 用来创建目标文件索引,这在创建较大的库时能提高速度。
也可以通过以下命令查看静态链接库内的文件:
ar -t libxxx.a
使用静态链接库时,除了需要库文件本身,还需要对应的头文件:库文件包含了真正的函数代码,也即函数定义部分;头文件包含了函数的调用方法,也即函数声明部分。
为了使用上面生成的静态链接库libtest.a,我们需要启用一个新的项目。任选一个目录,创建一个文件夹math,将math作为新项目的基础目录。
在比较规范的项目目录中;lib 文件夹一般用来存放库文件;include 文件夹一般用来存放头文件;src 文件夹一般用来存放源文件;bin 文件夹一般用来存放可执行文件。
为了规范,我们将前面生成的 libtest.a 放到 math 目录下的 lib 文件夹,将 test.h 放到 math 目录下的 include 文件夹。
在 math 目录下再创建一个 src 文件夹,在 src 中再创建一个 main.c 源文件。此时 math 目录中文件结构如下所示:
|-- include
| `-- test.h
|-- lib
| `-- libtest.a
|-- src
| `-- main.c
在 main.c 中,可以像下面这样使用 libtest.a 中的函数:
#include <stdio.h>
#include "test.h" //必须引入头文件
int main() {
int m, n;
printf("Input two numbers: ");
scanf("%d %d", &m, &n);
printf("%d+%d=%d\n", m, n, add(m, n));
printf("%d-%d=%d\n", m, n, sub(m, n));
printf("%d÷%d=%d\n", m, n, div(m, n));
return 0;
}
在编译 main.c 的时候,我们需要使用-I(大写的字母i)选项指明头文件的包含路径;使用-L选项指明静态库的包含路径;使用-l(小写字母L)选项指明静态库的名字。所以,main.c 的完整编译命令为:
gcc src/main.c -I include/ -L lib/ -l test -o math
注意,使用-l选项指明静态库的名字时,既不需要lib前缀,也不需要.a后缀,只能写 test,GCC 会自动加上前缀和后缀。
打开 math 目录,发现多了一个 math 可执行文件,使用./math命令就可以运行 math 进行数学计算。
root@xxx:~/projects/demo# ./math
Input two numbers: 12 23
12+23=35
12-23=-11
12÷23=0
动态链接出现的原因是为了解决静态链接中存在的两个问题:空间浪费和更新困难。
动态链接的基本思想是把程序按照模块拆分为各个相对独立部分,在程序运行时才将它们链接在一起形成一个完整的程序,而不是像静态链接那样在链接时将所有的模块组成一个可执行文件。
比如,有两个程序 program1.o 和 program2.o,它们有共同的依赖 libxxx.o,program1 先于 program2 执行,则 libxxx.o 会被加载到内存中,当 program2 执行时发现内存中已经存在 libxxx.o,则不再对其进行重新加载,而是将内存中已经存在的 libxxx.o 映射到 program2 的虚拟地址空间中,从而进行链接(这个链接过程和静态链接类似)形成可执行程序。
gcc -shared -fPIC *.c -o libtest.so
首先,我们需要生成可执行文件,此时的命令与静态链接相同:
gcc src/main.c -I include/ -L lib/ -l test -o math
此时,如果你直接使用./main执行程序,你会遇到这样的报错信息:
…/bin/math: error while loading shared libraries: libtest.so: cannot open shared object file: No such file or directory
这是因为没有指定动态链接库的所属位置。正确的命令是:
LD_LIBRARY_PATH="./lib" ./bin/math
LD_LIBRARY_PATH=: 告诉链接程序,从指定的目录下寻找链接库。
静态链接
动态链接
注:动态链接库地址生定位问题。
虽然动态链接把链接过程推迟到了程序运行时,但是在形成可执行文件时(注意形成可执行文件和执行程序是两个概念),还是需要用到动态链接库。比如我们在形成可执行程序时,发现引用了一个外部的函数,此时会检查动态链接库,发现这个函数名是一个动态链接符号,此时可执行程序就不对这个符号进行重定位,而把这个过程留到装载时再进行。
本文系转载,前往查看
如有侵权,请联系 cloudcommunity@tencent.com 删除。
本文系转载,前往查看
如有侵权,请联系 cloudcommunity@tencent.com 删除。
扫码关注腾讯云开发者
领取腾讯云代金券
Copyright © 2013 - 2025 Tencent Cloud. All Rights Reserved. 腾讯云 版权所有
深圳市腾讯计算机系统有限公司 ICP备案/许可证号:粤B2-20090059 深公网安备号 44030502008569
腾讯云计算(北京)有限责任公司 京ICP证150476号 | 京ICP备11018762号 | 京公网安备号11010802020287
Copyright © 2013 - 2025 Tencent Cloud.
All Rights Reserved. 腾讯云 版权所有