[C.C++] 【C语言】.c源文件从编译到链接生成可.exe执行程序的过程

82 0
Honkers 前天 16:35 来自手机 | 显示全部楼层 |阅读模式

推荐看《程序员的自我修养 - 链接、装载与库》更深入了解相关内容

1. 过程概览

编译到链接是c语言的翻译环境,c语言还有执行环境。

  1. 组成一个程序的每个源文件通过编译过程分别转换成目标代码(object code);
  2. 每个目标文件由链接器(linker)捆绑在一起,形成一个单一而完整的可执行程序;
  3. 链接器同时也会引入标准C函数库中任何被该程序所用到的函数,而且它可以搜索程序员个人的程序库,将其需要的函数也链接到程序中。

2. 编译与链接

2.1 预编译/预处理

总的来说都是文本操作,注释被删除替换成一个空格;#define定义的符号也会被删除,其它所有出现这个符号的地方都被替换成所代表的常量;头文件所有函数实现都会包含进来,总共大概一两万行,最后才是自己写的代码。

预编译后会产生一个对应的.i文件,不过要说明的是这个预编译的步骤在vs中可以看到,但后面的步骤基本只能在linux gcc编译器,使用指令一步步看。


在windows vs中查看预编译后产生的文件:

选择是然后运行程序,实际程序会编译失败,因为仅仅只进行了预处理这个步骤,不过可以在项目文件下的X64/Debug下看到一个对应的.i文件。

在linux下执行预处理选项指令 gcc -E test.c -o test.i(-E代表只进行预处理,-o test.i代表指定输出到这个文件),预处理完成之后就停下来,预处理之后产生的结果都放在test.i文件中。

2.2 编译

在linux下执行编译选项指令 gcc -S test.c,编译完成之后就停下来,结果保存在test.s中。

编译阶段会将c代码转换为汇编代码,汇编大致会经过四个阶段:

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

比如图中左边的这段代码,对应右边编译器一个个符号分析它们分别代表什么。


语法分析阶段会产生一个语法树:

语义分析应该是根据语法树进行,我也不是很了解。

符号汇总这个阶段很重要,编译器会将每个.c文件中的全局符号(函数名、全局变量)汇总起来,后面的汇编和链接都会涉及到符号的处理。

2.3 汇编

汇编阶段是将编译阶段产生的.s文件(汇编代码)转成二进制机器码文件。在linux下执行汇编指令 gcc -c test.c,汇编完成之后就停下来,结果保存在test.o中。

在这个阶段会根据前面编译阶段的符号汇总,产生一个符号表,如:


对于右边申明的Add函数,在符号表中的地址是无效的,后续不会使用这个无效的地址,而是使用左边的地址,符号表在连接阶段会使用。

2.4 链接

链接阶段链接器(linker)会链接所有产生的.obj / .o目标文件,以及链接库生成合并成一个二进制的.exe可执行程序文件,这个阶段会经历合并段表、合并符号表操作。

gcc编译器生成的二进制目标文件和二进制的可执行文件,都是按照elf文件形式组织的,合并段表要做的就是将所有.o文件中相同数据段进行合并在一起;每个文件产生的符号表最后也会合并在一起,符号表中无效的地址会被舍弃,使用有效的地址,因为链接器链接查找函数都是根据符号和地址的匹配关系去找的,不然没法找到;最后生成一个.exe文件。

add.c和test.c的符号表,其中0x0000,无效的地址值会被舍弃:

如果add.c中没有Add函数,那么符号表中没有对应的函数名Add和地址,那么会连接失败:

3. 执行/运行环境

  1. 程序必须载入内存中。在有操作系统的环境中:一般这个由操作系统完成。在独立的环境中,程序的载入必须由手工安排,也可能是通过可执行代码置入只读内存来完成。
  2. 程序的执行便开始。接着便调用main函数。
  3. 开始执行程序代码。这个时候程序将使用一个运行时堆栈(stack),存储函数的局部变量和返回地址。程序同时也可以使用静态(static)内存,存储于静态内存中的变量在程序的整个执行过程一直保留他们的值。
  4. 终止程序。正常终止main函数;也有可能是意外终止。

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?立即注册

×
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

中国红客联盟公众号

联系站长QQ:5520533

admin@chnhonker.com
Copyright © 2001-2025 Discuz Team. Powered by Discuz! X3.5 ( 粤ICP备13060014号 )|天天打卡 本站已运行