从程序源代码到可执行文件的生成需要经历四个步骤:预处理、编译、汇编、链接。接下来让我们来看下具体的过程。
(以hello.c程序为例)
程序的翻译环境、执行环境和执行的过程
任何一个ANSI C的实现中,都存在两个环境。
翻译环境:在这个环镜中代码被转换成可执行的机器指令
执行环境:它用实际代码的执行
程序执行的过程:
1.程序必须载入带内存中。
2.程序的执行便开始,接着便调用main函数.(main函数被_tmainCRTStartup调用,而_tmainCRTStarup被mainCRTStarup调用)
3.开始执行程序代码。这个时候程序将使用一个运行时的堆栈(stack),存储函数的局部变量和返回地址。程序同时也可以使用静态内存,存储静态内存中的变量在程序的整个执行过程一直保留他们的值。
4.终止程序,(正常终止;也有可能意外终止)
预处理
命令: gcc -E hello.c -o hello.i
预处理过程主处理源代码中的预处理指令。具体如下:
处理”#include“预编译指令,将包含的文件插入到该预编译指令的位置。
将所有”#define“定义的宏进行替换。
处理所有的条件预编译指令(#if、#ifndef、#ifdef….)
添加行号和文件名标识,以便于编译器编译时产生调试用的行号信息及用于编译时产生编译错误或警告时能够显示行号。
删除所有的注释
保留所有的”#pragma“编译器指令,编译器要用他们。
编译
命令:gcc -S hello.i -o hello.s /gcc -S hello.c -o hello.s
编译主要过程就是把预处理完的文件进行一系列词法分析、语法分析、语义分析及优化后产生相应的汇编代码文件。
词法分析:源代码程序被输入到扫描器,将代码的字符序列分割成一系列记号。(记号分为:关键字、标识符、字面量、字符串等和一些特殊符号)。
语法分析:对记号进行语法分析,从而产生语法树,在语法分析时,很多运算符号的优先级和含义都将被确定下来,如果出现括号不匹配,缺少操作符,编译器就会报告语法分析阶段的错误。
语义分析:包括声明,类型的匹配和类型的转换。(这里的语义分析是指静态语义,即编在编译期可以确定的语义)
代码生成
代码优化
汇编
命令:gcc -c hello.s -o hello.o/gcc -c hello.c -o hello.o
汇编器是将汇编代码转变成机器可以执行的指令,每一条汇编语句几乎都对应一条机器指令。它没有复杂的语法,也没有语义,只需根据汇编指令和机器指令对照表依次翻译。
链接
因为现代软件的开发过程往往很大,一个windows xp就有4500万行代码,所以大型软件往往拥有成千上万个模块,这些模块相互依赖又相互独立。按一个个模块开发就简单多了,代码容易阅读、理解、重用。每个模块可以进行单独开发、编译、测试,改变部分代码不需要整个进行编译。将这样一个个单独的模块拼接起来就成为链接。
链接主要包括地址和空间的分配、符号决议和重定位。
重定位:重新计算各个目标的地址过程被叫做重定位。
每个模块的源代码(如.c)文件经过编译器编译成目标文件(一般扩展名为.o或.obj),目标文件和库(Library)一起链接形成最终的可执行文件,而最常见的库就是运行时库。