从.c文件到可执行文件,其间经历了几步?
高级语言是偏向人,按照人的思维方式设计的,机器对这些可是莫名奇妙,不知所谓。那从高级语言是如何过渡到机器语言的呢?这可是一个漫长的旅途呀!
其中,得经历这样的历程:C源程序->预处理->编译->汇编->链接程序->可执行文件
1.预处理
读取c源程序,对其中的伪指令(以#开头的指令)和特殊符号进行处理。伪指令主要包括以下四个方面:
(1)宏定义指令,如#define Name TokenString,#undef等。对于前一个伪指令,预编译所要作得的是将程序中的所有Name用TokenString替换,但作为字符串常量的Name则不被替换。对于后者,则将取消对某个宏的定义,使以后该串的出现不再被替换。
(2)条件编译指令,如#ifdef,#ifndef,#else,#elif,#endif,等等。这些伪指令的引入使得程序员可以通过定义不同的宏来决定编译程序对哪些代码进行处理。预编译程序将根据有关的文件,将那些不必要的代码过滤掉。
(3)加载头文件,如#include"FileName"或者#include等。采用头文件的目的主要是为了使某些定义可以供多个不同的C源程序使用。因为在需要用到这些定义的C源程序中,只需加上一条#include语句即可,而不必再在此文件中将这些定义重复一遍。预编译程序将把头文件中的定义统统都加入到它所产生的输出文件中,以供编译程序对之进行处理。包含到c源程序中的头文件可以是系统提供的,这些头文件一般被放在/usr/include目录下。在程序中#include它们要使用尖括号(<>)。另外开发人员也可以定义自己的头文件,这些文件一般与c源程序放在同一目录下,此时在#include中要用双引号("")。
预编译是将.c
文件转化成 .i文件,
重定向使用的gcc命令是:gcc –E hello.c >hello.i
在预处理阶段是不做语法检查的。
2.编译阶段 :
需要进行三个步骤:词法分析、语法分析和语义分析
在linux环境中,输入命令:gcc–s
hello.c 参数c告诉gcc命令只进行编译,不做其他处理。命令运行结束后产生hello.o的目标文件。
3.汇编过程
编译过程实际上指把汇编语言代码翻译成目标机器指令的过程。对于被翻译系统处理的每一个C语言源程序,都将最终经过这一处理而得到相应的目标文件。目标文件中所存放的也就是与源程序等效的目标的机器语言代码。
输入命令:gcc –c hello.c
就会生成hello.o的目标文件。
4.链接过程
链接就是将不同部分的代码和数据收集和组合成为一个单一文件的过程,这个文件可被加载或拷贝到存储器执行.
链接可以执行与编译时(源代码被翻译成机器代码时),也可以执行与加载时(在程序被
加载器加载到存储器并执行时),甚至执行与运行时,由应用程序来执行.在现代系统中,
链接是由链接器自动执行的.
链接器分为:静态链接器和动态链接器两种.
(1).静态链接器
静态链接器以一组可重定位目标文件和命令行参数作为输入,生成一个完全链接的可以加载和运行的可执行目标文件作为输出.
静态链接器主要完成两个任务:
1>符号解析:目标文件定义和引用符号.符号解析的目的在于将每个符号引用和一个符号定义联系起来.
2>重定位:编译器和汇编器生成从地址零开始的代码和数据节.链接器通过把每个符号定义和一个存储器位置联系起来,然后修改所有对这些符号的引用,使得他们执行这个存储位置,从而重定位这些节.
(2)动态链接器
共享库是一个目标模块,在运行时,可以加载到任意的存储器地址,并在存储器中和一个程序链接起来.这个过程称为动态链接,是由动态链接器完成的.
共享库的共享在两个方面有所不同.首先,在任何给定的文件系统中,对于一个库只有一个.so文件.所有引用该库德可执行目标文件共享这个.so文件中的代码和数据,而不是像静态库的内容那样被拷贝和嵌入到引用它们的可执行的文件中.其次,在存储器中,一个共享库的.text只有一个副本可以被不同的正在运行的进程共享.