标准 C 中,任何一种 C 的实现都存在两种环境:翻译环境 & 执行环境
翻译环境:.c
源文件 -> (.obj
目标文件) -> .exe
可执行文件
执行环境:RUNNING .exe
文件时的外部环境——运行环境,共有两种
- 宿主环境 —— 存在操作系统
- 独立环境 —— 不存在操作系统,在嵌入式的开发中会遇到这种情况
翻译
翻译就是将单个或多个 .c
源文件编译成 .obj
目标文件,然后再将目标文件中需要调用的标准库函数,或程序员自己编写的函数库通过链接器链接起来,最终生成单一的 .exe
可执行程序。
翻译过程执行两种操作:“编译” 和 “链接”,
而编译本身也包含两种操作:“预处理器处理” 和 “编译器的语法解析”
预处理器的处理,即:文件的字面处理。当编译器读取到 .c
源文件后,便会执行预处理,将文件中的 #define
用相关联的数字或字母替换掉。
编译器的语法解析,这一过程就是将程序员编写的 “高级语言” 转化为 “汇编语言”。通过解析每条语句的语法逻辑,将其进行转换(翻译)。
PS: 从上面我们可以猜测,我们中间得到的 .obj
文件其内容可能就是汇编语法构成。只是猜测,emmm~
编译和链接
这里以 Windows 平台下的 gcc 编译器为例进行解释。
gcc -c
不进行链接操作,最终生成 “.obj” 文件
gcc
包含链接操作,最终生成 “.exe” 文件
>>> gcc main.c
只编译一个源文件,但它却是一个完整的 C 程序
编译 "a.c" 源文件,再进行最终的链接过程之前,会生成一个 ".obj" 文件,当链接完成之后,".obj" 文件删除,得到 ".exe" 文件
>>> gcc main. c a.c b.c
编译多个文件,它们共同组成一个完整的 C 程序
只要当编译的文件超过一个时,中间打酱油的 ".obj" 文件就会被保留,而且 ".exe" 文件依旧生成
目标文件 ".obj" 被保留,这就允许我们对程序进行修改,并只用对修改后的源文件再进行重新链接
>>> gcc main.o a.o b.c
将 b.c 源文件重新进行编译,并和 main.0 b.o 等目标文件进行链接,最终生成 ".exe" 文件
正如上面那个例子中所描述的那样。。
>>> gcc -c main.c
编译 "main.c" 源文件,只生成 "main.o" 目标文件,不会生成 ".exe" 可执行文件
即:"-c" 的存在让编译器不再执行链接过程!!!
>>> gcc -c main.c a.c b.c
编译 "main.c" "a.c" "b.c" 等源文件,只生成 "main.obj" "a.obj" "b.obj" 等目标文件,没有 ".exe" 文件!
>>> gcc main.obj a.obj b.obj
编译 "main.o" "a.c" "b.c" 等目标文件,链接生成 ".exe" 文件
等过上面几个案例的分析,我们可以看出
- 使用
gcc ...
命令,不用考虑源文件是.c
or.obj
文件,最终都会被链接生成.exe
文件 - 使用
gcc -c ...
命令,同样也不用考虑源文件,最终都会在被编译后生成.obj
文件
PS: 更多的 gcc 语法可自行在 dos 界面,使用 gcc --help
进行阅读学习 : )
执行
由于涉及到部分操作系统的内容,所以便简单的介绍下两种环境下的执行内容
宿主环境中,我们通过翻译后生成的 .exe
文件会和操作系统的一种小型启动器连接。当我们通过点击或命令行驱动 .exe
程序时,启动器便会将该程序加载到内存中 RUNNING
独立环境中,由于未存在操作系统,所以程序是需要直接和底层进行接触,并驱动硬件。这里我们就需要将程序进行手动加载,然后 RUNNING
以上的内容和思考,是单纯在学习时所得的,肯定会存在很大的局限性,以及不足。到这里文章并没有完结,只是剩余的部分,需要我们自己用实践和经历去完善和补足。
大家加油 : )