玖叶教程网

前端编程开发入门

一文搞懂预处理器与链接器(预处理 链接)

(图片来自Objected-Oriented Programming Using C++: An Elaborative Appraoch(https://icarus.cs.weber.edu/~dab/cs1410/textbook/1.Basics/compiler_op.html))

什么是预处理器和链接器?

预处理器(preprocessor )和链接器(linker)是编程中编译和链接代码过程中的两个不同组件。让我们深入了解每个组件。

预处理器

预处理器是编译的初始阶段。它的主要目的是处理不属于核心编程语言指令,但提供额外功能的指令。它在实际编译开始之前对源代码进行操作。

常见的预处理器指令包括宏定义(#define)、文件包含(#include)、条件编译(#ifdef、#ifndef、#if、#else、#endif)以及其他类似的指令。

预处理器的角色是在源代码经历编译之前对其进行操作。

链接器

链接器是编译之后的独立阶段。它接收编译器生成的已编译目标代码,并将其与其他必要的代码和库结合起来,生成最终的可执行程序。

链接器解析程序不同部分之间的引用,例如函数调用和变量引用。它将各种对象文件和库链接在一起,确保所有部分协调一致地组合在一起。

链接器负责解决未解析的符号问题并生成最终的可执行文件。

预处理器和链接器有什么区别?

执行时间

预处理器在实际编译之前运行,对源代码进行操作。

链接器在编译之后运行,将已编译的目标代码组合起来生成最终的可执行文件。

功能

预处理器处理与源代码本身相关的指令和操作,例如文本替换和文件包含。

链接器处理已编译代码的组合,解析程序不同部分之间的引用,并生成最终的可执行文件。

总之,预处理器在编译之前的源代码上进行操作,而链接器在编译阶段之后对已编译的目标代码进行操作。它们在将源代码转换为可执行程序的过程中扮演着不同的角色。

预处理器如何操作源代码?


(图片来自Objected-Oriented Programming Using C++: An Elaborative Appraoch(https://icarus.cs.weber.edu/~dab/cs1410/textbook/1.Basics/compiler_op.html))

预处理器使用指令操作源代码,这些指令以井号(#)开头。这些指令向预处理器提供如何在实际编译之前修改或包含源代码部分的说明。预处理器的一个常见用途是通过宏定义。下面给您一个使用#define指令的例子:

#include <stdio.h>// This is a macro definition

#define SQUARE(x) ((x) * (x))

int main()

{

int num = 5;

int result = SQUARE(num);

printf("The square of %d is %d\n", num, result);

return 0;

}


在这个例子中:

  • #include <stdio.h>指令是一个预处理器指令,它将标准输入/输出库的内容包含在源代码中。
  • #define SQUARE(x) ((x) * (x))指令定义了一个名为SQUARE的宏。这个宏接受一个参数x,并将其扩展为表达式((x) * (x))。它将传递给它的值平方。

预处理后,代码看起来像这样:

#include <stdio.h>

int main()

{

int num = 5;

int result = ((num) * (num));

printf("The square of %d is %d\n", num, result);

return 0;

}

预处理器将所有出现的SQUARE(num)替换为相应的宏扩展((num) * (num))。这是一个简单的例子,但它说明了预处理器如何在实际编译过程开始之前通过替换代码模式来操作源代码。

链接器如何将各种目标代码和库组合在一起?

链接器通过解析程序不同部分之间的引用,将各种目标代码和库组合在一起,生成一个可执行文件。让我们考虑一个简单的例子,其中包含多个源文件和一个库。


假设我们有三个源文件:

  1. main.c

#include <stdio.h>

extern void hello();// Declaration of a function defined in another file

int main()

{

printf("Main program\n");

hello(); // Call to the external function

return 0;

}

2. hello.c


#include <stdio.h>

void hello()

{

printf("Hello, World!\n");

}

3. library.c

#include <stdio.h>

void libraryFunction()

{

printf("Library Function\n");

}

现在,让我们将每个源文件编译成目标文件:


gcc -c main.c -o main.o

gcc -c hello.c -o hello.o

gcc -c library.c -o library.o

-c 选项告诉编译器生成未链接的对象文件。接下来,我们可以使用链接器将这些对象文件链接在一起:

gcc main.o hello.o library.o -o my_program

在这个例子中:

  • main.ohello.olibrary.o 是从各自的源文件生成的目标文件。
  • 链接器(在这种情况下是 gcc )将这些目标文件作为输入,并将它们组合起来创建可执行文件 my_program
  • main.c 中调用的 hello() 函数,链接器通过将其链接到 hello.o 中的 hello() 函数的实际实现来解析引用。
  • 如果存在来自库的外部函数,如 library.o 中的 libraryFunction(),链接器也会解析这些引用。

在链接之后,我们有一个单一的可执行文件(在这个例子中是my_program),它包含了所有源文件和库的编译代码。当你运行./my_program时,它将产生以下输出:

Main program

Hello, World!

(图片来自Objected-Oriented Programming Using C++: An Elaborative Appraoch(https://icarus.cs.weber.edu/~dab/cs1410/textbook/1.Basics/compiler_op.html))



荟萃知识,滋养你我。

发表评论:

控制面板
您好,欢迎到访网站!
  查看权限
网站分类
最新留言