编译+链接过程【自用】

本文详细介绍了C语言程序从源文件到可执行文件的形成过程,包括预编译、编译、汇编和链接四个阶段。在预编译阶段,头文件被包含,宏定义被替换,注释被删除;编译阶段将C代码转换为汇编语言;汇编阶段生成目标文件,里面是二进制指令;链接阶段合并符号表并解决地址引用,生成最终的可执行文件。如果在链接阶段出现符号表找不到符号的情况,会导致链接失败。程序执行时,操作系统负责将程序加载到内存,并从main函数开始执行。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

浅记一下,还有很多linux的知识要学,等想到要补充的东西再逐步完善这篇博客

可执行文件形成过程

请添加图片描述每个源文件通过编译器生成.obj文件(目标文件)
然后目标文件和链接库通过链接器生成可执行文件。(比如win系统,最后就生成.exe文件)
而编译器做的事又有:预编译、编译、汇编

命令行操作

1. 预编译命令

gcc test.c -E

预编译(预处理)便停下,此时屏幕会出现预编译结果

请添加图片描述

gcc test.c -E -o test.i

-o:output。输出。将结果输出到test.i的文件中

请添加图片描述

生成了test.i文件,这是一个中间文件

此时

vim test.i

可以看到里面就是预编译结果

test.i中是C语言
请添加图片描述

2. 编译命令

gcc test.i -S

仅编译便停下,生成test.s文件,里面是编译后的结果

test.s文件中是汇编语言

请添加图片描述

3. 汇编命令

gcc test.s -c

会生成test.o文件,其实就是目标文件

在windows系统下,目标文件是xxx.obj。

在linux系统下,目标文件是xxx.o

test.o里面是二进制指令,我们直接看是看不懂的
请添加图片描述

不过,xxx.o文件是elf格式
在linux系统下有一个工具readelf可翻译里面写的是什么:

readelf test.o -s

但是我用的mac还不知道mac下有没有什么命令可以查看。


给定程序供分析

test.c

#include <stdio.h> 
extern int Add(int, int);
int main() {
  int a = 10;
  int b = 20;
  int c = Add(a, b);
  printf("%d\n", c);
  return 0;
}

add.c

int Add(int x, int y) {
	return x + y;
}

结果如下:
请添加图片描述

预编译阶段

完成预处理指令,文本操作,此时仍是C语言代码

  1. 头文件包含

    #include – 预处理指令。在test.i前面一大堆代码其实就是把头文件内容复制进去了

  2. define定义符号的替换

    #define – 预处理指令。在test.i中没有了#define这行,所有define定义的符号都被替换

  3. 注释删除
    test.i中没有注释行

编译阶段

把C语言代码翻译成了汇编代码

  1. 词法分析
  2. 语法分析
  3. 语义分析
  4. 符号汇总

符号汇总会把全局变量符号都汇总出来

汇编阶段

把汇编指令翻译成了二进制指令

形成符号表

链接阶段

  1. 合并段表:把相同的东西合在一起
  2. 符号表的合并和重定位

关于符号表

  • 形成符号表:简单来说就是让全局的符号形成一个符号和地址的表:比如Add,因为这个符号是要跨文件使用的,test.c里要用,add.c里也要用,这些符号是要形成符号表的,但是add.c里面的临时变量x,y这些是不用的,因为它们只在add.c里面的一个函数里能用

  • 合并符号表

    合并的意思就是,我们来找相同的符号。
    比如Add符号,链接器找到两个Add。这时,链接器会发现,有一个Add的地址是有效的,有一个是无效的,此时链接器会丢弃无效的那个地址,保留有效的地址。

    其实简单来说就是把源文件弄在一起了。


如果我们把add.c里的的Add函数名改为ADD

int ADD(int x, int y) {
	return x + y;
}

那么就会有如下报错
请添加图片描述从2、3行可以看到其实此时目标文件test-dfdbb1.o都已经生成了,到了4行ld阶段就不行了(ld是gcc的链接器)

linker command failed

说明链接阶段出了问题,就是因为符号表中找不到ADD符号,链接失败了

像这种函数名、变量名写错的情况,都是在链接阶段失败了

程序执行的过程

程序执行的过程:

  1. 程序必须载入内存中。在有操作系统的环境中:一般这个由操作系统完成。在独立的环境中,程序
    的载入必须由手工安排,也可能是通过可执行代码置入只读内存来完成。

  2. 程序的执行便开始。接着便调用main函数。

  3. 开始执行程序代码。这个时候程序将使用一个运行时堆栈(stack),存储函数的局部变量和返回
    地址。程序同时也可以使用静态(static)内存,存储于静态内存中的变量在程序的整个执行过程
    一直保留他们的值。

  4. 终止程序。正常终止main函数;也有可能是意外终止。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值