在ANSI(美国国家标准协会)C的任何一种实现中,存在两种环境:
1. 翻译环境(Translation environment):负责将源代码转换成可执行的机器指令。
2. 执行环境(Execution environment):用于实际执行。
这两种环境可以不在同一个机器上。
预处理, 展开头文件/宏替换/去掉注释/条件编译 (test.i main .i)
编译, 检查语法,生成汇编 ( test.s main .s)
汇编, 汇编代码转换机器码 (test.o main.o)
链接 链接到一起生成可执行程序 a.out
翻译
翻译阶段有几个步骤组成,组成一个程序的每个(可能很多)源文件通过编译器过程分别转换成目标代码(object code),然后各个文件由链接器(Linker)捆绑在一起,形成单一而完整的程序,链接器同时会引入标准C函数库中的任何被该程序所用到的函数,而且它也可以搜索程序员个人的程序库,将其中需要使用的函数也链接到程序中,如图2.1所示。
编译过程也有几个阶段组成:
(1)首先是预处理(preprocessor),由预处理器在源代码上执行一些文本操作,用实际值代替#define定义的符号和#include指令包含的文件的内容;
(2)解析(Parse):源代码经过解析,判断他句子的意思。本阶段是产生绝大多数错误和警告信息的地方(也就是编译期错误),
(3)编译产生目标代码。目标代码是机器指令的初步形式,用于实现程序的语句。若我们在编译程序的命令行中加入要进行优化的选项,优化器(Optimizer)就会对目标代码进行处理,使他效率更高。优化过程需要额外的时间,所以在程序调试完毕并准备生成正式产品之前一般不进行这个过程。至于目标代码是直接产生的,还是先以汇编语言语旬的形式存在,然后再 经过一个独立的阶段编译成 目标文件,对我们来说并不重要。
静态链接是由链接器在链接时将库的内容加入到可执行程序中的做法。链接器是一个独立程序,将一个或多个库或目标文件(先前由编译器或汇编器生成)链接到一块生成可执行程序。静态链接是指把要调用的函数或者过程链接到可执行文件中,成为可执行文件的一部分。
动态链接所调用的函数代码并没有被拷贝到应用程序的可执行文件中去,而是仅仅在其中加入了所调用函数的描述信息(往往是一些重定位信息)。仅当应用程序被装入内存开始运行时,在Windows的管理下,才在应用程序与相应的DLL之间建立链接关系。当要执行所调用DLL中的函数时,根据链接产生的重定位信息,Windows才转去执行DLL中相应的函数代码。(在此可执行文件被执行时,动态链接库的全部内容将被映射到运行时相应进程的虚地址空间。动态链接程序将根据可执行程序中记录的信息找到相应的函数代码。)
两者比较:对于可执行文件中的函数调用,可分别采用动态链接或静态链接的方法。使用动态链接能够使最终的可执行文件比较短小,并且当共享对象被多个进程使用时能节约一些内存,因为在内存中只需要保存一份此共享对象的代码。但并不是使用动态链接就一定比使用静态链接要优越。在某些情况下动态链接可能带来一些性能上损害。
编译和链接
用于编译和链接C程序的 特定命令在不同的系统中各不相同。在绝大多数Unix系统中,C编译器称为cc,可使用多种不同方法来调用:
1. 编译并链接一个完全包含于一个源文件的 C 程序:
cc program.c
这条命令产生一个称为 a.out 的可执行程序。中间会产生一个名为 program.o 的目标文件,但它 在链接过程完成后会被删除。
2 . 编译并链接几个 C 源文件:
cc main.c sort.c lookup.c
当编译的源文件超过一个时,目标文件便不会被删除。这就允许你对程序进行修改后,只对那 些进行过改动的源文件进行重新编译,如下 条命令所示。
3 . 编译一个 C 源文件,并把它和现存的目标文件链接在一起:
cc main.a lookup.a sort.c
4 . 编译单个 C 源文件,并产生一个 目标文件 (本例中为 program.o ),以后再进行链接:
cc -c program.c
5 . 编译几个 C 源文件,并为每个文件产生一个 目标文件:
cc -c main.c sort.c lookup.c
6. 链接几个目标文件:
cc main.o sort.o lookup.o
用于 MS-DOS 和 Windows 的Borland CIC++ 5.0 有两种用户界面,你可以分别选用。Windows
集成开发环境是一个完整的独立编程工具,它包括源代码编辑器、调试器和编译器。它的具体使用 不在本书的范围之内。MS-DOS 命令行界面则与 UNIX 编译器差不太多,只是有下面几点不同:
1. 它的名字是 bcc。
2 . 目标文件的名字是 file.obj 。
3 . 当单个源文件被编译并链接时,编译器并不删除目标文件。
4 . 在缺省情况下,可执行文件以命令行中第一个源或目标文件名命名,不过你可以使用 “-ename” 选项把可执行程序文件命名为 “name.exe”。
编译详细过程可参考:
一:预处理
使用-E选项:gcc -o test.i -E test.c
预处理阶段主要做了处理“#”的过程:有头文件展开,宏替换,条件编译,去掉注释等。二:编译
使用-S选项:gcc -o test.s -S test.c
编译阶段将高级语言翻译成机器语言,生成对应的汇编代码。三:汇编
使用-c选项:gcc -o test.o -c test.c
汇编阶段主要将源文件翻译成二进制文件。四:链接
链接过程主要将翻译成的二进制文件与需要用到的库链接。
执行
程序的执行过程也需要经历几个阶段。首先,程序必须载入到内存中。在宿主环境中 ( 也就是 具有操作系统的环境),这个任务由操作系统完成。那些不是存储在堆战中的尚未初始化的变量将在
这个时候得到初始值。在独立环境中,程序的载入必须由于工安排 ,也可能是通过把可执行代码置 入只读内存(ROM)来完成。
然后,程序的执行便开始。在宿主环境中,通常一个小型的启动程序与程序链接在一起。它负
责处理一系列 日常事务,如收集命名行参数以便使程序能够访 问它们。接着,便调用 main 函数。 现在,便开始执行程序代码。在绝大多数机器里,程序将使用一个运行时堆桔(staek),它用于
存储函数的局部变量和返回地址。程序同时也可以使用静态(statie)内存,存储于静态 内存中的变量
在程序的整个执行过程中将一直保留它们的值。 程序执行的最后一个阶段就是程序的终止,它可以由多种不 同的原因引起。“正常” 终止就是
main 函数返回。有些执行环境允许程序返回一个代码,提示程序为什么停止执行。在宿主环境中, 启动程序将再次取得控制权,并可能执行各种不同的日常任务,如关闭那些程序可能使用过但并未
显式关闭的任何文件。除此之外,程序也可能是由于用户按下 break 键或者电话连接的挂起而终止, 另外也可能是由于在执行过程中出现错误而自行中断。
参考文献:
[1] https://blog.youkuaiyun.com/han8040laixin/article/details/81559727
[2] https://blog.youkuaiyun.com/weixin_41143631/article/details/81221777
[3] https://blog.youkuaiyun.com/u012662731/article/details/78520349