小白也能吃透 C 语言编译链接流程

程序的一生:翻译环境 vs 运行环境

先想象一个场景:你写了一段 C 代码(比如printf("Hello World!")),

电脑要怎么「理解」并运行它?

这就需要了解程序的两个关键阶段:翻译环境运行环境

翻译环境:从代码到可执行文件的工厂

作用

1.把人类能看懂的 C 代码,翻译成计算机能执行的二进制指令(.exe.out等可执行文件)。

        就像是美钞只能在美国商店内买东西一样。 

类比

类似「翻译官」把中文说明书翻译成英文,计算机只认识二进制的「机器语言」,翻译环境就是那个「翻译官」。

运行环境:程序的舞台

作用:真正运行可执行文件的环境,负责加载程序、分配内存、执行指令等。
类比:翻译好的英文说明书要交给工人使用,运行环境就是那个「工厂车间」。

翻译环境详解:四个步骤拆解(预编译→编译→汇编→链接)

1. 预编译(预处理):先做「杂活」的小工

任务

        1.处理#include头文件:把stdio.h的内容「粘贴」到代码里(类似盖房子前先搬来砖块)

        2.处理#define宏定义:把代码中的MAX=100替换成100(像把图纸上的「房间 A」换成具体尺寸)。

         3.删除注释:注释是给人看的,计算机不需要,直接删掉(类似擦掉图纸上的备注)。

输入文件.c源文件(如main.c
输出文件:预处理后的.i文件(内容变得超级长,都是展开后的代码)。

        如果此时你用gcc编译器打开main.i,你就会发现你是能看懂代码的,

        其内操作只是将#include所用的库全部搬过来了,并且将#define定义的宏全部放到所用地

例子

预处理前:
#include "stdio.h"
#define ADD(a,b) a+b
int main() {
    // 计算1+2
    printf("%d", ADD(1,2)); 
    return 0;
}

预处理后(简化版):
[一堆代码,是库内用来定义stdio.h所用的*************用********************进行省略***********************************************
****************************************************************
***********************************************

********************************************************
***************************************************************************
******************
***************************************************************************************************************************************************************************************************************************************************************************************************
*************************************************************************************************************************
***************************************************************************************************************
*****************************************************************************************************************
*************************************************************************************************************************************************************************
**********************
****************************************************************************************************************************************************************************************************************************************************************************************************]
int main() {
    printf("%d", 1+2); // 注释被删除,宏被替换
    return 0;
}

 

2. 编译:把代码「翻译」成高级图纸

任务
将预处理后的 C 代码翻译成汇编语言(人类勉强能看懂的低级语言,类似「施工图」)。

检查语法错误:如果代码写错别字(如prinft),这一步会报错(类似图纸审核发现尺寸错误)。

优化代码结构:让程序运行更高效(如合并重复指令)。

输入文件.i文件
输出文件:汇编.s文件(如main.s)。

例子
预处理后的 C 代码 → 编译 → 汇编代码(类似中文说明书 → 翻译成英文简单句):

; 汇编代码片段(简化)
mov edi, offset s        ; 加载"Hello World!"的地址
call printf              ; 调用printf函数

3. 汇编:把高级图纸变成「零件」 

任务
将汇编语言翻译成机器语言(二进制指令),生成目标文件.o.obj)。

        每一条汇编指令对应一条或多条机器指令(类似把施工图拆解成螺丝、钉子等零件)。

        目标文件是二进制文件,但还不能直接运行,

        因为可能缺少某些「零件」(如printf函数的实现) 

输入文件.s汇编文件
输出文件:目标.o文件(如main.o)。

例子
汇编代码 .s → 汇编 → 二进制 .o(类似英文简单句 → 拆成单词字母组合):

二进制片段(示例):
11010101 00101010 11100011 ...(计算机能识别的0和1)

4. 链接:组装所有零件成房子

任务
把多个目标文件(.o)和库文件(如printf所在的 C 标准库)合并成可执行文件.exe.out)。

        解决「符号引用」:比如代码中调用了printf,需要找到它在库中的具体地址(类似盖房子时发现缺少窗户,去建材市场买窗户装上)

        处理多个文件关联:如果有main.ctool.c,需要把它们的目标文件合并(类似把不同房间的零件组装成完整房子)

例子 

链接过程:
main.o中调用printf的地方 ↓
---------------------------
| 符号未定义:需要找到printf的地址 |
---------------------------
↓ 去标准库中找到printf的二进制代码,填入地址

为什么需要链接?一个关键问题:库函数从哪来?

假设你写了一句printf("Hello"),C 语言本身没有实现printf,它存放在C 标准库中。

预编译时,#include 只是声明了printf的存在(告诉编译器「这里有个函数叫 printf」)。

链接时,编译器才会去标准库中找到printf的实际二进制代码,「粘」到可执行文件里(类似盖房子时需要买现成的门窗装上去)。

库文件就相当于别人已经将该函数方法写好了,并以文件的方式存储在  .h的文件中,比如stdio.h

这就是一个头文件,你用函数的时候所使用的方法就是从里面进行调用。

复习要点总结

四步流程对比表

步骤 输入文件 输出文件 核心作用 类比
预编译 .c .i 处理宏、头文件、注释 搬砖 + 拆备注
编译 .i .s 翻译成汇编语言,查语法错误 画施工图
汇编 .s .o 翻译成二进制目标文件 生产零件
链接 .o + 库文件 可执行文件 合并文件,解决符号引用 组装零件成房子

脑图记忆法(自己动手画更印象深刻!)

翻译环境四步曲
├─ 预编译(杂活小工):宏展开、头文件粘贴、删注释
├─ 编译(翻译官):C代码→汇编代码,查语法
├─ 汇编(零件工厂):汇编→二进制目标文件
└─ 链接(组装师傅):目标文件+库→可执行文件

参考《程序员的自我修养》的关键知识点 

目标文件的三种类型

可重定位文件(.o):可以和其他文件链接(类似可组装的零件)。

可执行文件(.exe):可以直接运行(组装好的完整房子)。

共享库文件(.so.dll):类似「通用零件库」,多个程序可共用。

链接的两种方式

静态链接:把库代码直接复制到可执行文件中(房子自带所有零件,体积大)。

动态链接:运行时再去加载库(房子需要时去零件库借,体积小,更灵活)。

 

你可能感兴趣的:(c+初阶学习,c语言,开发语言,linux,服务器,数据结构)