1. 编辑器: 我们编写代码的一些窗口,如:记事本、word、notepad等。
2. 编译器: 检查用户代码的一些语法错误并且将其编译成汇编代码。
3.汇编器:将编译出来的文件变成目标代码(windows 下的.obj文件)
4.连接器:将目标代码连接成为可执行文件(.exe),及双击就可以运行文件。
5.集成开发环境(Integrated Development Environment, 简称IDE):是用于程序开发环境的应用程序。一般包括代码编辑器、编译器、调试器和图形用户界面工具。如:VC6.0、C_Free等。
其他目标代码-》
(.c .cpp)源代码-》编译器-》(.s)汇编代码-》汇编器-》(.o)目标代码-》链接器-》可执行程序
库文件-》
编译的完整过程:
C源程序-》 预编译处理(.c)-》编译、优化程序(.s .asm)->汇编程序(.obj、 .o、.a、 .ko)-->链接程序(.exe、 .elf、 .axf 等)
1. C源程序
自己编写的程序代码
2. 预编译处理(.c)
包括四个过程
a. 宏定义指令, 如#define N 6, #undef 等
对于前一个伪指令,预编译所要做的是将程序中的所有N用6替换,请大家注意这里是替换、并不是像作为函数那样将6复制进N这个变量。对于后者,则将取消对某个宏的定义,使以后出现的N不再被替换。
b.条件编译指令,如#ifdef, #ifndef, #endif 等。
这些伪指令的引入使得程序员可以通过定义不同的宏来决定编译程序对哪些代码进行处理。预编译程序将根据有关的文件,将那些不必要的代码过滤掉。这样就能在编译阶段减少编译时间,提高效率,看看这是多好的指令。
c. 头文件包含指令,如#include "file.h" 或#include <file.h>等
在头文件中一般用伪指令#define 定义了大量的宏(最常见的是字符常量),同时包含有各种外部符号的声明。
采用这样的做法一来可以让我们直接调用一些复杂库函数;二来可以免去我们在写程序时重复做一些定义声明工作的麻烦。试想一下,一旦我们写好头文件,那么以后要用到相关模块就再也不用写这些函数了, 直接#include 就OK了,这可是一劳永逸啊。
#include<> 告诉编译器去系统默认的路径寻找相关的文件。
#include"": 告诉编译器先去源程序所在目录下寻找,如果没有就去系统默认路径寻找。
d. 特殊符号,预编译程序可以识别一些特殊的符号。
例如在源程序中出现的LINE标识将被解释为当前行号(十进制数),FILE则被解释为当前被编译的C源程序的名称。预编译程序就是对在源程序中出现的这些特殊符号将用合适的值进行替换。
大家注意到没,预编译阶段基本上是完成对源程序的相关代码进行替换,这样之后程序的原意没有改变,就是代码的内容有所不同,这样为以后的编译做好准备。
3. 编译、优化程序(.s、 .asm)
经过上一阶段的处理,现在我们的程序已经没有宏定义,包含头文件等指令了,只剩下一些变量、常量、关键字等,而编译的主要作用是检查这些代码的语法错误及将这些代码编译成为汇编文件。
4. 汇编程序(.obj、 .o、 .a、 .ko)
在这个阶段是将汇编代码翻译成目标文件,这时的文件已经是二进制代码了。在windows环境下文件的后缀名是.obj; 在unix下则有是o、 .a、 .ko 等文件。
目标文件由段组成。通常一个目标文件中至少有两个段:
代码段: 该段中所包含的主要是程序的指令。该段一般是可读和可执行的,但一般却不可写。
数据段: 主要存放程序中要用到的各种全局变量或静态的数据。一般数据段都是可读,可写,可执行的。
5. 链接程序(.exe、 .elf、 .axf)
被包含的头文件,以及当我们的程序分布于很多源文件时,那么这些源文件该怎么处理呢,这就是连接器的作用,它们被翻译成目标代码后需要被链接到一起才能被执行。
谈到函数库的链接,我们还需要了解点函数库的知识,函数库分静态链接库(又称静态库*.lib) 和链接动作库(又称动态库 *.dll)
静态库的链接在编译时会被编译进汇编文件,这样的操作会改变文件大小;而动态库则是在执行时(双击运行),当需要动态库中的文件时,才被链接到可执行文件的。