c++ “静态链接”和“动态链接”是什么意思?

pw136qt2  于 2023-05-08  发布在  其他
关注(0)|答案(5)|浏览(200)

我经常听到术语“静态链接”和“动态链接”,通常是指用CC++C#编写的代码。它们是什么,它们到底在谈论什么,它们在联系什么?

w80xi6nr

w80xi6nr1#

从源代码(您编写的代码)到可执行代码(您运行的代码)有两个阶段(在大多数情况下,不包括解释代码)。
第一种是编译,它将源代码转换为目标模块。
第二种是链接,它将目标模块组合在一起以形成可执行文件。
区别在于,允许第三方库包含在您的可执行文件中,而无需查看其源代码(例如用于数据库访问,网络通信和图形用户界面的库),或者用于编译不同语言的代码(例如C和汇编代码),然后将它们链接在一起。
当您将文件静态链接到可执行文件时,该文件的内容将在链接时包含在内。换句话说,文件的内容被物理地插入到您将要运行的可执行文件中。
当你链接 * 动态 *,一个指向被链接的文件的指针(文件的文件名,例如)被包含在可执行文件中,并且所述文件的内容在链接时不被包含。只有当您稍后运行可执行文件时,这些动态链接的文件才会被引入,并且它们只被引入可执行文件的内存副本中,而不是磁盘上的副本中。
它基本上是一种延迟链接的方法。还有一个更延迟的方法(在某些系统上称为延迟绑定),它不会引入动态链接的文件,直到您实际尝试调用其中的函数。
静态链接的文件在链接时被“锁定”到可执行文件,因此它们永远不会更改。可执行文件引用的动态链接文件可以通过替换磁盘上的文件来更改。
这允许在不必重新链接代码的情况下更新功能;加载程序每次运行时都会重新链接。
这有好有坏-一方面,它允许更容易的更新和错误修复,另一方面,如果更新不兼容,它可能导致程序停止工作-这有时是一些人提到的可怕的“DLL地狱”的原因,即如果您将动态链接库替换为不兼容的库,应用程序可能会崩溃(顺便说一句,这样做的开发人员应该会被追捕并受到严厉的惩罚)。
作为一个 * 示例 *,让我们看一下用户为静态和动态链接编译main.c文件的情况。

Phase     Static                    Dynamic
--------  ----------------------    ------------------------
          +---------+               +---------+
          | main.c  |               | main.c  |
          +---------+               +---------+
Compile........|.........................|...................
          +---------+ +---------+   +---------+ +--------+
          | main.o  | | crtlib  |   | main.o  | | crtimp |
          +---------+ +---------+   +---------+ +--------+
Link...........|..........|..............|...........|.......
               |          |              +-----------+
               |          |              |
          +---------+     |         +---------+ +--------+
          |  main   |-----+         |  main   | | crtdll |
          +---------+               +---------+ +--------+
Load/Run.......|.........................|..........|........
          +---------+               +---------+     |
          | main in |               | main in |-----+
          | memory  |               | memory  |
          +---------+               +---------+

在静态情况下,可以看到主程序和C运行时库在链接时(由开发人员)链接在一起。由于用户通常无法重新链接可执行文件,因此他们只能使用库的行为。
在动态的情况下,主程序与C运行时导入库(声明动态库中的内容,但实际上并不 * 定义 * 它)链接。这允许链接器即使在缺少实际代码的情况下也进行链接。
然后,在运行时,操作系统加载器进行主程序与C运行时DLL(动态链接库或共享库或其他命名法)的后期链接。
C运行时的所有者可以随时添加新的DLL来提供更新或错误修复。如前所述,这既有优点也有缺点。

whlutmcx

whlutmcx2#

我认为这个问题的一个好答案应该解释什么是链接。
例如,当你编译一些C代码时,它会被翻译成机器语言。只是一个字节序列,当运行时,会导致处理器进行加,减,比较,“后藤”,读内存,写内存,诸如此类的事情。这些内容存储在对象(.o)文件中。
很久以前,计算机科学家发明了“子程序”这个东西。执行此代码块并返回此处。没过多久,他们就意识到最有用的子程序可以存储在一个特殊的地方,允许任何需要它们的程序使用。
在早期,程序员必须输入这些子程序所在的内存地址。类似于CALL 0x5A62。如果这些存储器地址需要更改,这将是乏味和有问题的。
所以,这个过程是自动化的。你写了一个调用printf()的程序,编译器不知道printf的内存地址。所以编译器只是写CALL 0x0000,并在对象文件中添加一个注解,说“必须用printf的内存位置替换这个0x0000”。
静态链接意味着链接器程序(GNU的链接器程序称为ld)将printf的机器码直接添加到您的可执行文件中,并将0x0000更改为printf的地址。这发生在创建可执行文件时。
动态链接意味着上述步骤不会发生。可执行文件 still 有一个注解,说“必须用printf的内存位置替换0x000”。操作系统的加载器需要找到printf代码,将其加载到内存中,并在每次运行程序时纠正CALL地址。
程序通常会调用一些静态链接的函数(像printf这样的标准库函数通常是静态链接的)和其他动态链接的函数。当可执行文件运行时,静态的“成为”可执行文件的一部分,而动态的“加入”。
这两种方法都有优点和缺点,并且在操作系统之间存在差异。但既然你没问我就到此为止吧。

qmelpv7a

qmelpv7a3#

静态链接库在编译时被链接。动态链接库在运行时加载。静态链接将库位烘焙到可执行文件中。动态链接只在对库的引用中进行烘焙;动态库的位存在于别处,并且可以在以后被换出。

s8vozzvw

s8vozzvw4#

因为上面的帖子 * 实际上都没有展示如何 * 静态链接一些东西,看看你做得对不对,所以我将解决这个问题:
一个简单的C程序

#include <stdio.h>

int main(void)
{
    printf("This is a string\n");
    return 0;
}

动态链接C程序

gcc simpleprog.c -o simpleprog

然后在二进制文件上运行file

file simpleprog

这将显示它是动态链接的,沿着以下路线:

simpleprog: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), dynamically linked (uses shared libs), for GNU/Linux 2.6.26, BuildID[sha1]=0xf715572611a8b04f686809d90d1c0d75c6028f0f, not stripped

这一次,让我们静态地链接程序:

gcc simpleprog.c -static -o simpleprog

在此静态链接的二进制文件上运行文件将显示:

file simpleprog

现在的结果将是

simpleprog: ELF 64-bit LSB executable, x86-64, version 1 (GNU/Linux), statically linked, for GNU/Linux 2.6.26, BuildID[sha1]=0x8c0b12250801c5a7c7434647b7dc65a644d6132b, not stripped

你可以看到它是静态链接的。然而不幸的是,并不是所有的库都可以简单地以这种方式静态链接,并且可能需要使用libtool或手动链接目标代码和C库来进行扩展。
幸运的是,许多嵌入式C库(如musl)为几乎所有的库提供了静态链接选项。
现在strace你已经创建的二进制文件,你可以看到在程序开始之前没有访问过库:

strace ./simpleprog

现在与strace在动态链接程序上的输出进行比较,您将看到静态链接版本的strace要短得多!

gk7wooem

gk7wooem5#

(我不懂C#,但有一个VM语言的静态链接概念很有趣)
动态链接涉及到知道如何找到所需的功能,你只有一个从你的程序引用。您的语言运行时或操作系统在文件系统、网络或编译代码缓存中搜索一段代码,匹配引用,然后采取几种措施将其集成到内存中的程序映像中,例如重新定位。它们都在运行时完成。它可以手动完成,也可以由编译器完成。有能力更新与混乱的风险(即DLL地狱)。
静态链接是在编译时完成的,你告诉编译器所有的功能部分在哪里,并指示它集成它们。没有搜索,没有歧义,没有能力更新而不重新编译。你的所有依赖项都与你的程序映像物理上是一个。

相关问题