前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >专栏 >静态链接和动态链接

静态链接和动态链接

作者头像
麦克马
修改于 2025-05-28 08:11:19
修改于 2025-05-28 08:11:19
13800
代码可运行
举报
文章被收录于专栏:C/C++C/C++
运行总次数:0
代码可运行

一、前言

编译型语言生成可执行文件的过程包括编译和链接。编译包括预处理、编译和汇编三个步骤。而链接则根据发生时间不同分为静态链接和动态链接。那么让我们来看看整个过程具体是如何执行的,这里以Linux环境下的c/c++语言为例。

二、编译

  • 预处理:主要对源文件中以 # 开头的语句,例如宏、#inclue、预编译指令 #ifndef 等进行处理,生成的是 *.i 文件;
  • 编译:主要进行词法分析、语法分析和语义分析,生成 *.s 汇编文件;
  • 汇编:将汇编指令翻译为机器指令,生成可重定位二进制目标文件 *.o。

三、静态链接

上面提到链接分为静态链接和动态链接,静态链接先于动态链接出现,动态链接属于计算机发展较后期出现的技术。两者没有优劣之分,只是各自有各自适用场景。

3.1 链接

实际开发过程中,将所有代码都放在一个源文件中对维护和管理不是很友好,因此会有多个源文件存在,且不同的源文件之间相互有依赖关系。如,不同源文件之间进行函数调用。

但在编译时期每个源文件都是单独编译,并生成各自对应的 *.o 文件,所以还需要有个将这些目标文件合并在一起生成一个可执行文件的过程。而这个过程就是静态链接(动态链接出现之后对链接的叫法)。

3.2 原理

由多目标文件链接形成静态库,反之静态库也可以简单看成是一组目标文件的集合。

链接器在进行链接时以目标文件为单位。如果同一目标文件中包括很多函数,当我们仅需要调用其中单个函数时也会连同其他函数一起链接过来,造成内存空间的浪费,因此应尽量一个源文件中只包含一个函数。

制作链接库的目的是希望他人在使用我们的功能实现的同时不会拿到源代码,这种机制尤其对 商用软件非常友好。

Linux 中静态链接库文件命名规则为:libxxx.a,其中 xxx 代表库的名称,lib为固定前缀, .a 为固定后缀,如 libc.a。前缀和后缀在链接过程中由链接器自动追加。

3.3 静态库生成过程

  • 首先使用 gcc 命令将源文件编译成目标文件:gcc -c 源文件列表
  • 然后使用 ar 命令将目标文件打包成静态库:
代码语言:shell
AI代码解释
复制
ar rcs libxxx.a 目标文件列表

ar 是 Linux 的一个备份压缩命令,它可以将多个文件打包成一个备份文件(也叫归档文件),也可以从备份文件中提取成员文件。ar 命令最常见的用法是将目标文件打包为静态链接库。对参数的说明: r 用来替换库中已有的目标文件,或者加入新的目标文件。 c 表示创建一个库。不管库否存在,都将创建。 s 用来创建目标文件索引,这在创建较大的库时能提高速度。

也可以通过以下命令查看静态链接库内的文件:

代码语言:shell
AI代码解释
复制
ar -t libxxx.a

3.4 静态库使用

使用静态链接库时,除了需要库文件本身,还需要对应的头文件:库文件包含了真正的函数代码,也即函数定义部分;头文件包含了函数的调用方法,也即函数声明部分。

为了使用上面生成的静态链接库libtest.a,我们需要启用一个新的项目。任选一个目录,创建一个文件夹math,将math作为新项目的基础目录。

在比较规范的项目目录中;lib 文件夹一般用来存放库文件;include 文件夹一般用来存放头文件;src 文件夹一般用来存放源文件;bin 文件夹一般用来存放可执行文件。

为了规范,我们将前面生成的 libtest.a 放到 math 目录下的 lib 文件夹,将 test.h 放到 math 目录下的 include 文件夹。

在 math 目录下再创建一个 src 文件夹,在 src 中再创建一个 main.c 源文件。此时 math 目录中文件结构如下所示:

代码语言:shell
AI代码解释
复制
|-- include
|  `-- test.h
|-- lib
|  `-- libtest.a
|-- src
|  `-- main.c

在 main.c 中,可以像下面这样使用 libtest.a 中的函数:

代码语言:c
代码运行次数:0
运行
AI代码解释
复制
#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 的完整编译命令为:

代码语言:shell
AI代码解释
复制
gcc src/main.c -I include/ -L lib/ -l test -o math

注意,使用-l选项指明静态库的名字时,既不需要lib前缀,也不需要.a后缀,只能写 test,GCC 会自动加上前缀和后缀。

打开 math 目录,发现多了一个 math 可执行文件,使用./math命令就可以运行 math 进行数学计算。

代码语言:shell
AI代码解释
复制
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 的虚拟地址空间中,从而进行链接(这个链接过程和静态链接类似)形成可执行程序。

4.1 创建动态链接库

代码语言:shell
AI代码解释
复制
gcc -shared -fPIC *.c -o libtest.so
  • shared:标志着要生成动态链接库;
  • fPIC:告诉编译器产生与位置无关的代码。

4.2 使用动态链接库

首先,我们需要生成可执行文件,此时的命令与静态链接相同:

代码语言:shell
AI代码解释
复制
gcc src/main.c -I include/ -L lib/ -l test -o math

此时,如果你直接使用./main执行程序,你会遇到这样的报错信息:

代码语言:shell
AI代码解释
复制
…/bin/math: error while loading shared libraries: libtest.so: cannot open shared object file: No such file or directory

这是因为没有指定动态链接库的所属位置。正确的命令是:

代码语言:shell
AI代码解释
复制
LD_LIBRARY_PATH="./lib" ./bin/math

LD_LIBRARY_PATH=: 告诉链接程序,从指定的目录下寻找链接库。

五、静态链接和动态链接对比

静态链接

  • 优点 由于在可执行文件中已经具备所有执行文件所需要的东西,在执行的时候运行速度快。
  • 缺点 浪费空间,因为每个可执行程序中对所有需要的目标文件都要有一份副本,所以如果多个程序对同一个目标文件有依赖,如多个程序中都调用了printf()函数,则这多个程序中都包含printf.o,所以同一个目标文件在内存中存在多个副本。 更新困难,因为每当库函数的代码发生修改,这个时候就需要重新编译所有依赖这个库函数的程序,形成新的可执行文件。

动态链接

  • 优点 节省空间资源:共享同一个依赖库 更新便利:只需替换相关的模块即可
  • 缺点 性能受损:因为把链接推迟到了程序运行时,所以每次执行程序都需要进行链接,所以性能会有一定损失。 据估算,动态链接和静态链接相比,性能损失大约在5%以下。经过实践证明,这点性能损失用来换区程序在空间上的节省和程序构建和升级时的灵活性是值得的。

注:动态链接库地址生定位问题。

虽然动态链接把链接过程推迟到了程序运行时,但是在形成可执行文件时(注意形成可执行文件和执行程序是两个概念),还是需要用到动态链接库。比如我们在形成可执行程序时,发现引用了一个外部的函数,此时会检查动态链接库,发现这个函数名是一个动态链接符号,此时可执行程序就不对这个符号进行重定位,而把这个过程留到装载时再进行。

本文系转载,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文系转载,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 一、前言
  • 二、编译
  • 三、静态链接
    • 3.1 链接
    • 3.2 原理
    • 3.3 静态库生成过程
    • 3.4 静态库使用
  • 四、动态链接
    • 4.1 创建动态链接库
    • 4.2 使用动态链接库
  • 五、静态链接和动态链接对比
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档