程序的编译与链接(C语言为例) #代码写好后到运行期间要经过怎样的过程呢?# 粗略版 #

简介: 程序的编译与链接(C语言为例) #代码写好后到运行期间要经过怎样的过程呢?# 粗略版 #

前言


每当我们运行一段代码时,编译器都会自动的帮我们编译代码并将代码转换为一个二进制可执行文件(.exe), 有了这个可执行文件,便可以执行我们写的程序了。那么编译器对代码的编译以及生成可执行程序的过程是怎样的呢?这个问题便是本文章将要探讨的。


程序的环境


ANSI C的任何一种实现中,存在两个不同的环境,一种是翻译环境,一种是执行环境

  • 翻译环境:在这个环境中源代码被转换为可执行的机器指令;
  • 执行环境:这个环境用于实际执行代码。


补充:


244e6e4f9e3a49e892f881df3d688960.png


程序的编译与链接


首先看看 翻译环境 的简图:


b2b929047ad5427c9dd95ef1ca144056.png


组成一个程序的每个源文件通过编译过程分别转换成目标代码(object code)。

每个目标文件由链接器(linker)捆绑在一起,形成一个单一而完整的可执行程序。

链接器同时也会引入标准C函数库中任何被该程序所用到的函数,而且它可以搜索序员个人的程序库,将其需要的函数也链接到程序中。


编译本身也分为几个阶段:

预编译 —> 编译 —> 汇编


db32ddf8a0ae4100852a281f5e4cb1c9.png


预编译(预处理)


预编译又叫预处理。预编译不是编译,而是编译前的处理,编译器正式开始编译程序之前,会执行一段预处理程序(又称预处理器)专门对程序执行预处理操作。

预处理阶段要做的事情主要是这些:


  1. #include 头文件的包含;
  2. #define定义符号的替换和删除;
  3. 注释的删除。


接下来在linux中用gcc编译器对一段代码进行预处理,如下:

cf553d8421754b2d867cd0384868a30a.png

预处理过后,我们观察test.i这个预编译后的文件:

指令:gcc -E test.c -o test.i


e07d8daef4ed48b2a6186d73d0c05844.png


可以看到,代码一下子膨胀了许多,这正是因为头文件被包含进来了,当来原先的#define与注释也不见了。


  • 编译

编译阶段是将C语言代码翻译成汇编代码, 其过程有:

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

符号汇总就是将相关的函数,以及全局变量汇总:

例如以下代码汇总后客观图:

edd45eedd38b4e188709bb472e9a0d6e.png


将上述代码编译后:

指令:gcc -S test.c -o test.s

d8dc9a5b6685470c8df7284d982e6e36.png

可以看到,C语言被翻译成了汇编代码。


  • 汇编
  1. 汇编是将汇编代码翻译成了二进制指令(存放目标文件),也就是生成目标文件的一步(test.o
  2. 汇编使汇总的符号形成符号表,也就是每个符号对应一个地址。


如下:



0dc360efdb524943a8e63703b494cc81.png


就将上述代码汇编,我们来看看test.o是不是二进制文件呢?

指令:gcc -c test.c -o test.o

3c21440fbd34429f90740e14489b69f2.png

可以看到,的确是一些二进制乱码。


通过编译的一系列过程后,接下来就是链接了


链接的相关过程有:


1. 合并段表(这里不解释,需了解细读《程序员的自我修养》这本书,里面对整个编译链接部分都有很详细的讲解)

2. 符号表的合并和重定位。


这里只讲解符号表的合并:


  1. 在上面所探讨的编译过程,每一个文件都会形成自己的目标文件,在汇编这一步,又会形成自己的符号表;
  2. 如果一个程序有两个文件,就会有两个符号表,所以,符号表的合并,就是链接的一步。

例如test1.c文件和test2.c文件的符号表合并过程:


fedfed3afe1d4375a04bcd114c53bfd8.png


最终,通过链接器和链接库将各个目标文件链接后形成可执行文件。


程序执行的过程:


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

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

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

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


写在最后


如果说,你对这一块特别感兴趣,想继续深入,你可以读《程序员的自我修养》这本书,这本书里对这一块的知识有很详细的解析。


感谢阅读本小白的博客,错误的地方请严厉指出噢!

相关文章
|
1天前
|
存储 Linux C语言
C语言进阶第十一节 --------程序环境和预处理(包含宏的解释)-2
C语言进阶第十一节 --------程序环境和预处理(包含宏的解释)
|
1天前
|
自然语言处理 Linux 编译器
C语言进阶第十一节 --------程序环境和预处理(包含宏的解释)-1
C语言进阶第十一节 --------程序环境和预处理(包含宏的解释)
|
2天前
|
传感器 算法 C语言
C语言在嵌入式系统开发中的优化策略与代码实现
C语言在嵌入式系统开发中的优化策略与代码实现
12 1
|
6天前
|
存储 算法 C语言
C语言进阶:顺序表(数据结构基础) (以通讯录项目为代码练习)
C语言进阶:顺序表(数据结构基础) (以通讯录项目为代码练习)
|
6天前
|
编译器 Linux C语言
C语言:预处理详解(知识点和代码演示)
C语言:预处理详解(知识点和代码演示)
|
6天前
|
存储 自然语言处理 编译器
C语言:编译和链接(从.c文件到输出结果的过程)
C语言:编译和链接(从.c文件到输出结果的过程)
|
6天前
|
C语言
换硬币问题(C语言代码练习)
换硬币问题(C语言代码练习)
|
9天前
|
C语言
以c语言为基础实现的简易扫雷游戏(游戏代码附在文章最后,如有需要请自取)
以c语言为基础实现的简易扫雷游戏(游戏代码附在文章最后,如有需要请自取)
31 1
|
16天前
|
存储 算法 Serverless
22年+21年 计算机能力挑战赛初赛C语言程序题 题解
22年+21年 计算机能力挑战赛初赛C语言程序题 题解
22 2
|
16天前
|
编译器 C语言 C++
c语言中代码运行时发生c++弹窗的解决方法
通过遵循这些步骤,你应该能够解决在C语言代码运行时出现C++弹窗的问题,确保代码在纯C环境中运行。 买CN2云服务器,免备案服务器,高防服务器,就选蓝易云。百度搜索:蓝易云
18 0