程序员的自我修养之程序的编译和链接

243 阅读2分钟

持续创作,加速成长!这是我参与「掘金日新计划 · 6 月更文挑战」的第3天,点击查看活动详情

这一系列是《程序员的自我修养》的阅读笔记:

程序员的自我修养之程序的编译和链接

程序员的自我修养之目标文件里有什么

前言

多年前,我是一个对计算机一窍不通的小孩,跟着慕课网的教材敲出C语言的第一个 Hello World 的程序并成功运行,眼里除了惊喜还有疑惑:它是如何实现的呢?

我们编写一个 hello.c 程序。

#include <stdio.h>

int main()
{
    printf("Hello World\n");
    return 0;
}

在Linux下,当我们使用GCC来编译Hello World程序时,会得到一个可执行文件a.out。

$gcc hello.c
$./a.out
Hello World

从一个C语言程序到一个可执行文件,这中间有下面四个过程。

  1. 预编译
  2. 编译
  3. 汇编
  4. 链接

预编译

$gcc -E hello.c -o hello.i

或者

$cpp hello.c > hello.i

预编译过程主要处理那些源代码文件中的以“#”开始的预编译指令。比如“#include”、“#define”等,主要处理规则如下:

  • 将所有的“#define”删除,并且展开所有的宏定义。
  • 处理所有条件预编译指令,比如“#if”、“#ifdef”、“#elif”、“#else”、“#endif”。
  • 处理“#include”预编译指令,将被包含的文件插入到该预编译指令的位置。注意,这个过程是递归进行的,也就是说被包含的文件可能还包含其他文件。
  • 删除所有的注释“//”和“/* */”。
  • 添加行号和文件名标识,比如#2“hello.c”2,以便于编译时编译器产生调试用的行号信息及用于编译时产生编译错误或警告时能够显示行号。
  • 保留所有的#pragma编译器指令,因为编译器须要使用它们。

编译

$gcc -S hello.i -o hello.s

编译过程就是把预处理完的文件进行一系列词法分析、语法分析、语义分析及优化后生产相应的汇编代码文件,这个过程往往是我们所说的整个程序构建的核心部分,也是最复杂的部分之一。

汇编

$as hello.s -o hello.o

或者

$gcc -C hello.c -o hello.o

汇编器是将汇编代码转变成机器可以执行的指令,每一个汇编语句几乎都对应一条机器指令。

链接

链接指的是将目标文件和其他一些文件链接起来得到可执行文件。

image.png

图中是将两个目标文件和library库链接得到可执行文件。这是最基本的链接过程。最常见的库就是运行时库(RuntimeLibrary),它是支持程序运行的基本函数的集合。库其实是一组目标文件的包,就是一些最常用的代码编译成目标文件后打包存放。