C程序的编译和链接初步讲解

218 阅读2分钟

一个c程序运行的流程:

大致流程:

image.png

进一步

每个.c文件都会单独经过编译器处理生成自己的目标文件。

windows下,目标文件后缀为.obj;

linux下,目标文件后缀为.o.

然后【所有目标文件】和链接库通过链接器处理,生成可执行程序

image.png

再进一步

编译也具体拆分成3个阶段 —— 预编译、编译、汇编。

image.png

一 预编译

1 执行各种预处理指令。如#include的头文件包含,对#define定义的符号进行替换等

2 注释的删除

3 生成对应的.i文件

【.i文件就是,对源文件预处理后,生成的文件】

例如:text.c -> text.i

二 编译

1 把c语言代码翻译成汇编代码

2 生成对应的.s文件

例:text.i -> text.s

此时text.s里面的代码是汇编代码

编译过程具体包括:

   语法分析
   词法分析
   语义分析
   符号汇总

其中(符号汇总)是编译期间将.i文件中出现的全局符号梳理出来,例如函数名.

例:

//以下是不同文件的代码
//text.c
#include <stdio.h>
extern void print1(void);
int main()
{
    print1();
    printf("hehe\n");
    return 0;
}

//print1.c
void print1(void)
{
    printf("haha\n");
}

此时在text.i会汇总main 和 print1;

在print1.i会汇总print1

三 汇编

1 将汇编指令翻译成二进制指令

2 生成对应的目标文件,后缀.obj(windows下)

例:test.s -> test.obj

3 形成符号表,给编译期间汇总的符号赋一个有序或无效的地址。

例:

text.c地址原因
print1无效地址因为print在另一个文件定义
main0x5f933179该函数就在text.c中
printf无效地址因为该函数有部分在链接库中,在<stdio.h>并不完整
print1.c地址
print10x6a893496

四 链接

1 合并段表

通俗讲,每个目标文件都按照某种相同的格式排版

当把【所有目标文件】和链接库链接生成可执行程序

该程序的格式也和目标文件一样。

2 符号表的合并和重定位

将每个目标文件的符号表和链接库进行合并,此时就能覆盖print1和printf的无效地址

函数地址
main0x5f933179
printf0x5c578946
print10x6a893496

这解决了外部符号如何跨文件使用的问题。