编译器、链接器、装载器是什么?

121
我想深入了解编译器、链接器和装载器的含义和工作原理,最好是以 C++ 为例。

请看:https://dev59.com/-HVC5IYBdhLWcg3wYQAp#311889 - paxdiablo
14个回答

205
=====> COMPILATION PROCESS <======

                     |
                     |---->  Input is Source file(.c)
                     |
                     V
            +=================+
            |                 |
            | C Preprocessor  |
            |                 |
            +=================+
                     |
                     | ---> Pure C file ( comd:cc -E <file.name> )
                     |
                     V
            +=================+
            |                 |
            | Lexical Analyzer|
            |                 |
            +-----------------+
            |                 |
            | Syntax Analyzer |
            |                 |
            +-----------------+
            |                 |
            | Semantic Analyze|
            |                 |
            +-----------------+
            |                 |
            | Pre Optimization|
            |                 |
            +-----------------+
            |                 |
            | Code generation |
            |                 |
            +-----------------+
            |                 |
            | Post Optimize   |
            |                 |
            +=================+
                     |
                     |--->  Assembly code (comd: cc -S <file.name> )
                     |
                     V
            +=================+
            |                 |
            |   Assembler     |
            |                 |
            +=================+
                     |
                     |--->  Object file (.obj) (comd: cc -c <file.name>)
                     |
                     V
            +=================+
            |     Linker      |
            |      and        |
            |     loader      |
            +=================+
                     |
                     |--->  Executable (.Exe/a.out) (com:cc <file.name> ) 
                     |
                     V
            Executable file(a.out)

C 预处理器 :-

C预处理是编译的第一步,它处理:

  1. #define 语句。
  2. #include 语句。
  3. 条件语句。
  4. 宏定义。

该单元的目的是将C源文件转换为纯C代码文件。

C编译 :

该单元有六个步骤:

1) 词法分析器:

它将源文件中的字符组合成“标记”。标记是一组不带有“空格”,“制表符”和“换行符”的字符。因此,这个编译单元也被称为“令牌化器”。它还删除注释,生成符号表和重定位表条目。

2) 句法分析器:

这个单元检查代码中的语法。例如:

{
    int a;
    int b;
    int c;
    int d;

    d = a + b - c *   ;
}

以上代码会产生解析错误,因为等式不平衡。本单元通过以下方式生成解析树来内部检查它:
                            =
                          /   \
                        d       -
                              /     \
                            +           *
                          /   \       /   \
                        a       b   c       ?

因此,这个单元也被称为解析器。

3)语义分析器:

这个单元检查语句的含义。例如:

{
    int i;
    int *p;

    p = i;
    -----
    -----
    -----
}

以上代码生成了"Assignment of incompatible type"错误。

4) 预优化:

该单元独立于CPU,即有两种类型的优化:

  1. 预优化(与CPU无关)
  2. 后优化(与CPU有关)

该单元按以下形式优化代码:

  • I)死代码消除
  • II)子代码消除
  • III)循环优化

I)死代码消除:

例如:

{
    int a = 10;
    if ( a > 5 ) {
        /*
        ...
        */
    } else {
       /*
       ...
       */
    }
}

在这里,编译器在编译时知道了'a'的值,因此它也知道if条件语句总是为真。因此,它消除了代码中的else部分。

II) 子代码消除:

例如:

{
    int a, b, c;
    int x, y;

    /*
    ...
    */

    x = a + b;
    y = a + b + c;

    /*
    ...
    */
}

可以优化如下:

{
    int a, b, c;
    int x, y;

    /*
     ...
    */

    x = a + b;
    y = x + c;      // a + b is replaced by x

    /*
     ...
    */
}

III) 循环优化:

例如:

{
    int a;
    for (i = 0; i < 1000; i++ ) {

    /*
     ...
    */

    a = 10;

    /*
     ...
    */
    }
}

在上述代码中,如果'a'是本地的且在循环中未被使用,则可以进行以下优化:
{
    int a;
    a = 10;
    for (i = 0; i < 1000; i++ ) {
        /*
        ...
        */
    }
}

5) 代码生成:

在这个阶段,编译器生成汇编代码,使得更频繁使用的变量存储在寄存器中。

6) 后优化:

在这个阶段,优化是与CPU相关的。例如,如果代码中有多个跳转,则将它们转换为一个:

            -----
        jmp:<addr1>
<addr1> jmp:<addr2>
            -----
            -----

控制直接跳转到。

然后,最后一个阶段是链接(创建可执行文件或库)。当运行可执行文件时,所需的库会被加载。


11
我不确定为什么还没有人指出链接器和装载器不是同一个步骤。链接是编译的一部分,而加载是(在)运行程序的一部分。 - SimpleGuy
1
  1. 汇编器和最重要的8) 链接器和9) 装载程序在哪里被要求了?
- user985399
@SimpleGuy,“编译”和“链接”不是两个不同的概念吗?我认为通常人们使用术语“构建”来指代编译和链接等操作。 - starriet

144
  • 编译器读取、分析并将代码转换为目标文件或错误消息列表。
  • 链接器将一个或多个目标文件和可能的一些库代码组合成可执行文件、库或错误消息列表。
  • 加载器读取可执行代码到内存中,进行地址转换并尝试运行程序,结果可能是正在运行的程序或错误消息(或两者都有)。

ASCII表示:

[Source Code] ---> Compiler ---> [Object code] --*
                                                 |
[Source Code] ---> Compiler ---> [Object code] --*--> Linker --> [Executable] ---> Loader 
                                                 |                                    |
[Source Code] ---> Compiler ---> [Object code] --*                                    |
                                                 |                                    |
                                 [Library file]--*                                    V
                                                                       [Running Executable in Memory]

2
这个解释很明了。你应该考虑成为讲师。谢谢。 - 0.sh
1
谢谢您不回避关于链接器和装载器的问题! - user985399

31

希望这能对您有所帮助。

首先,浏览以下图示:

(img source->internet)

source->internet

您编写一段代码并保存文件(源代码),然后:

预处理:顾名思义,不是编译的一部分。它们指示编译器在实际编译之前执行所需的预处理。您可以将此阶段称为文本替换或解释由#表示的特殊预处理指令。

编译:编译是将用一种语言编写的程序翻译成另一种目标语言的过程。如果存在错误,编译器将检测到并报告。

汇编:汇编代码被翻译成机器代码。您可以将汇编器称为一种特殊类型的编译器。

链接:如果这些代码片段需要链接其他源文件,则链接器将它们链接起来形成可执行文件。

完成这些步骤后还有许多进程。没错,你猜对了,这里就是加载器的作用:

加载器:它将可执行代码加载到内存中;程序和数据堆栈被创建,寄存器被初始化。

附加信息:http://www.geeksforgeeks.org/memory-layout-of-c-program/,您可以在那里查看内存布局。


16

编译器:它是一种将高级语言程序翻译为机器语言程序的程序。编译器比汇编器更智能,检查各种限制、范围、错误等。但它的程序运行时间更长,占用内存更大,速度较慢。因为编译器会对整个程序进行处理,然后将整个程序翻译成机器代码。如果编译器在计算机上运行,并为同一台计算机生成机器代码,则称为自编译器或常驻编译器。另一方面,如果编译器在计算机上运行,并为其他计算机生成机器代码,则称为交叉编译器。

链接器:在高级语言中,存储了一些内置的头文件或库。这些库是预定义的,包含执行程序所必需的基本函数。这些函数由一个名为链接器的程序连接到库中。如果链接器找不到某个功能的库,则通知编译器,然后编译器生成错误。编译器在编译程序的最后一步自动调用链接器。除了预定义库外,它还将用户定义的功能链接到用户定义的库中。通常,较长的程序被分成较小的子程序,称为模块。这些模块必须组合起来才能执行程序。链接器负责组合这些模块的过程。

加载器:加载器是一种将程序的机器代码加载到系统内存中的程序。在计算机领域,操作系统的一部分是负责加载程序的程序。它是启动程序过程中必不可少的步骤之一,因为它将程序放置到内存中,并准备执行。加载程序涉及将可执行文件的内容读入内存中。加载完成后,操作系统通过将控制权传递给加载的程序代码来启动程序。所有支持程序加载的操作系统都有加载器。在许多操作系统中,加载器常驻内存。


14

维基百科应该有一个很好的答案,这是我的想法:

  • 编译器: 读取something.c源代码,将其写入something.o对象文件。
  • 链接器: 将多个*.o文件组合成可执行程序。
  • 加载器:将可执行文件加载到内存中并开始运行。

5

针对Linux/Unix操作系统解释,尽管这是所有其他计算机系统的基本概念。

来自LinuxJournal的Linkers和Loaders以清晰的方式解释了这个概念。它还解释了经典名称a.out是如何得来的(汇编输出)。

一个快速总结:

c程序 --> [编译器] --> 目标文件 --> [链接器] --> 可执行文件(比如a.out)

我们得到了可执行文件,现在把这个文件给你的朋友或需要该软件的客户 :)

当他们运行此软件时,例如在命令行中输入./a.out

在命令行中执行./a.out --> [装载器] --> [execve] --> 程序被加载到内存中

一旦程序加载到内存中,通过将PC(程序计数器)指向a.out的第一条指令,将控制权转移到该程序。


4

编译器:

它会读取源文件,可能是 .c 或 .cpp 等类型,并将其翻译成称为目标文件(.o 文件)的文件。

链接器:

它将为多个源文件生成的多个 .o 文件组合成可执行文件(在 GCC 中为 ELF 格式)。有两种类型的链接:

  • 静态链接
  • 动态链接

装载程序:

一个程序,可以将可执行文件加载到机器的主存中。


如果您想详细学习 Linux 中这三个程序执行阶段,请阅读此文


3
  • 编译器:将人类可理解格式转换为机器可理解格式。
  • 链接器:将机器可理解格式转换为操作系统可理解格式。
  • 加载器:实际上将程序加载并运行到RAM中的实体。

链接器和解释器互相排斥。解释器逐行获取代码并逐行执行。


1

编译器会检查您的源代码中的错误并将其转换为目标代码。这是操作系统运行的代码。

通常您不会在单个文件中编写整个程序,因此链接器会链接所有目标代码文件。

除非程序在主存储器中,否则无法执行。


1
  • 编译器:一种语言翻译器,将完整的程序转换成机器语言,生成计算机可以完整处理的程序。
  • 链接器:实用程序,将一个或多个已编译的目标文件合并成可执行文件或另一个目标文件。
  • 加载器:将可执行代码加载到内存中,创建程序和数据堆栈,初始化寄存器并启动代码运行。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接