程序环境和预处理
程序的翻译环境和执行环境
程序的编译也叫做程序的翻译,主要可以分为这四个步骤:预编译、编译、汇编、链接。
在ANSIC的任何一种实现中,存在两个不同的环境。
- 第1种是翻译环境,在这个环境中源代码被转换为可执行的机器指令。
- 第2种是执行环境,它用于实际执行代码。
#include <iostream>
using namespace std;
int main()
{
cout<<"hello world"<<endl;
}
一份C语言源代码,(假设名称为 main.c) ,需要通过,预编译、编译、汇编、链接生成可执行文件(即可以运行的文件)。可执行文件的文件后缀为 .exe 文件。
- 在Windows中C语言源代码生成的可执行文件的扩展名一般是
.exe
- 在Linux中C语言源代码生成的可执行文件的扩展名一般是
.out
。
预编译、编译、汇编、链接
预编译
预编译也叫做预处理。会生成 .i 文件,通过gcc -E main.c -o main.i 生成.i文件将进行如下操作:
- 将所有的 #define 删除,并展开宏定义。
- 处理所有的预编译指令,如:#if,#elif,#else,#endif。
- 处理#include预编译指令,将被包含的头文件插到预编译指令的位置。
- 添加行号信息,文件名标识,便于调试。
- 删除所有的注释。
- 保留所有的#gragma编译指令。
- 生成 main.i 文件(包括注释,宏替换,头文件展开,条件编译),编译生成的 main.i 文件不包含任何的宏定义,因为宏已经被展开,并且被插入到 .i 文件中。
编译(C/C++语音 ------> 汇编):
通过gcc -S main.i –o main.s 生成.s文件,需要进行如下操作:
-
扫描、词法分析、语法分析、语义分析、源代码分析、目标代码生成、目标代码优化。
-
词法分析
词法分析会分析你的代码中的所有符号,然后产生一系列不同类型的记号:标识符、特殊符号(比如运算符号)、数字、字符串等。
-
语法分析
运算符的优先级和含义也被定下来。在这个阶段,括号不匹配,缺少操作符等问题就会被编译器发现,然后报告语法错误。
-
语义分析
编译器可以分析的语义是静态语义。包括声明和类型的匹配,类型的转换等。
//举例子: // 当一个浮点类型的数据被赋值给整形数据时,其中隐含了一个浮点类型到整形数据的转换,语义分析的过程需要完成这个步骤。 // 将一个浮点值赋值给一个指针的时候,编译器会发现类型不匹配,然后报编译错误。
动态语义一般是值在运行的时候出现的语义相关的问题,比如0作为除数时是一个运行时语义错误。
-
-
生成汇编代码。
-
符号汇总。
- 在词法分析的时候我们得到了很多的符号。在整个编译与链接的过程中,我们将函数名和变量名作为他们对应的符号名。
-
生成.s文件。
汇编(汇编 ------> 二进制)
汇编是利用汇编器将汇编代码转化成机器可以执行的指令。每一个汇编指令几乎都对应了一条机器指令。
通过gcc –c main.s –o main.o 生成.o文件,(windows环境中目标文件的后缀是.obj、在linux`环境目标文件是后缀是.o)
汇编需要进行如下操作:
1、根据汇编指令和特定平台,把汇编指令翻译成二进制形式。
2、合并各个section,合并符号表。
3、生成.o文件。
汇编后得到的文件就是目标文件。
-
目标文件就是源代码编译后但是还没有进行链接的中间文件。
-
目标文件中有编译后的机器指令代码,数据。除此以外,目标文件中还有链接时需要的一些信息:符号表,调试信息,字符串等。
-
一般目标文件将这些信息按照不同的属性,以段segment的形式存储形成符号表。(一般情况下,他们都表示一个一定长度的区域)。
//符号表中有什么? //符号表中记录了每一个被汇总的符号,以及该符号的地址。如果这个符号是一个还没有被定义的函数名,那么这个地址就不是一个游戏地址,但是符号表中仍然有这个符号的数据。
-
这是整个编译汇编过程中十分重要的一步。每一个文件编译完后都会有一个对应的符号表存储在目标文件中。
-
每一个目标文件都会有一个符号表,这个表中记录了目标文件中所用到的所有符号,每一个定义的符号有一个对应的值,叫做符号值。对于函数和变量来说这个符号值就是它们的地址。
-
符号表的作用在链接的时候体现。
链接
链接过程会进行如下操作:
1、合并各个.obj文件的section,合并符号表,进行符号解析。
2、符号地址重定位。
3、生成可执行文件。
链接依靠的是链接器—为了让我们使用库函数有源头。现代的编译器可以将一个源代码编译成一个未链接的目标文件,然后由链接器将这些目标文件链接起来形成一个可执行文件。
我们的两个文件在连接前是不能够运行出结果的。因为在main函数调用sum函数的时候无法找到准确的地址。而链接的作用就可以简单理解为帮助程序去找到外部符号的地址
怎样找到外部符号的地址呢?
链接器通过符号表的合并和符号表的重定位做到这一点。
这样在调用外部符号的时候就可以找到准确的地址了。
链接器通过符号表的合并和符号表的重定位做到这一点。
————————————————
版权声明:本文为CSDN博主「头疼的太阳花」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/cainiaochufa2021/article/details/125661575