1、程序的翻译环境和执行环境
在C语言中的任何一种实现中,都存在着两种环境。
- 翻译环境,在这个环境下源代码被转换为可执行的机器指令。
- 运行环境,实际执行代码。
2、编译和链接
2.1、一个被隐藏的过程
以下是我们熟知的一个程序。
#include<stdio.h>
int main()
{
printf("Hello World\n");
return 0;
}
我们在编译器中通过编译运行能很轻松的在我们屏幕上打印成功,
但事实上,在编译这个阶段其实还包括了 预处理、编译、汇编、链接。
如图:
从大概来看
- 程序的源文件通过编译过程转换为目标代码,生成目标文件(.obj(win环境下)或.o(linux环境))。
- 如果有多个源文件,链接中的链接器会将它们捆绑在一起,最终形成一个可执行文件。
- 链接将多个源文件和链接库一起,最终生成一个可执行文件。链接库为我们提供其它需要引入的库函数。
2.2、编译过程的细节
2.2.1、预处理
在预处理过程主要处理那些源代码文件中的以#开头的预编译指令,如#include、#define
规则如下.
- 将所有的 ” #define “删除,并且展开所有的宏定义(全部替换)。
- 处理条件预编译指令,如 #if、#ifdef、#elif、#else、#endif。
- 处理” #include “预编译指令,将被包括的文件插入该预编译指令的位置。注意这个过程是递归进行的,也就是一个文件里可能会包含另一个文件。
- 删除所有的注释。
- 保留所有的#pragma 编译器指令,因为编译器需要使用到它们。
经过预处理后的.i文件不再包含任何宏定义。
2.2.2、编译
在编译的过程中,会经历4个过程,词法分析、语法分析、语义分析、符号汇总,将源代码转换成汇编代码之后存放在.s文件中。
每个过程中:
- 词法分析: 通过一个叫lex的程序将源代码的字符序列分割成一系列的记号,这个记号有标识符、关键字、字面量(如数字、字符串)。
- 语法分析: 通过语法分析器将记号进行语法分析,形成语法树,完成对一些表达式的语法分析。
- 语义分析: 通过语义分析器来对语法是否有意义进行分析(比如指针相乘是没有意义的,但在语法分析中是合法的,所以语义分析的是有无意义)。
- 符号汇总: 会将源代码中的全局变量以及函数汇总出来,形成一个表,方便在后续链接中使用。
比如对Test.c源文件,将Test.c里面的Add和main汇总。
对add.c,将add.c里的Add汇总。
2.2.3、汇编
汇编将汇编代码转换成二进制指令,生成目标文件(.obj(win环境下)或.o(linux环境))。
值得注意的是这个过程会形成符号表,在之前编译过程中,我们对全局变量以及函数进行了汇总,在这里我们通过给汇总里的成员添加一个地址,就形成了一个符号表。
符号表在后续中的链接会使用。
2.2.4、链接
在链接过程中有两个动作,一个是合并段表,还个是符号表的合并和重定位。
1.合并段表
在汇编后形成的.o文件,它里面的数据在linux环境下是由 elf 这种格式进行存放的,这种 elf 格式会将文件分成很多个段,每个段存放着不同的数据。
如果是多个.o文件(目标文件),前面说了会和链接库进行链接最终形成可执行文件(.exe),而可执行文件也是 elf 这种格式,所以之前的多个.o文件中被 elf 格式分割的相同的段就需要合并。
2.符号表的合并和重定位
这个操作中,合并了汇编中的符号表的地址,以及重定位了个别成员的地址。
比如:
这将确保了在程序运行,我们使用的函数有效,但如果不存在 Add 函数,那么合并的将会是一个无效的地址,在程序执行后就会发生链接型错误。
比如:
3、执行
程序执行的过程:
- 程序必须载入内存中。在有操作系统的环境中:一般这个由操作系统完成。在独立的环境中,程序
的载入必须由手工安排,也可能是通过可执行代码置入只读内存来完成。 - 程序的执行便开始。接着便调用main函数。
- 开始执行程序代码。这个时候程序将使用一个运行时堆栈(stack)(这里可参考函数栈帧的创建和销毁),存储函数的局部变量和返回地址。程序同时也可以使用静态(static)内存,存储于静态内存中的变量在程序的整个执行过程一直保留他们的值。
- 终止程序。正常终止main函数;也有可能是意外终止。
本章完。
今天的文章程序翻译有( )和( )两种方式_编译程序和汇编程序的区别分享到此就结束了,感谢您的阅读。
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。
如需转载请保留出处:https://bianchenghao.cn/88501.html