网站建设 有聊天工具的吗,烟台网站建设开发,公司想做个自己的网站怎么做,王也平在本文章里面#xff0c;我们讲会讲解C语言程序是如何从我们写的代码一步步变成计算机可以执行的二进制指令#xff0c;并最终执行的。C语言程序运行主要包括两大步骤 -- 编译和链接#xff0c;接下来我们就来一一讲解。 目录
1 翻译环境和运行环境
2 翻译环境 1#… 在本文章里面我们讲会讲解C语言程序是如何从我们写的代码一步步变成计算机可以执行的二进制指令并最终执行的。C语言程序运行主要包括两大步骤 -- 编译和链接接下来我们就来一一讲解。 目录
1 翻译环境和运行环境
2 翻译环境 1 预处理预编译
2 编译
1 词法分析
2 语法分析
3 语义分析
3 汇编
4 链接 1 翻译环境和运行环境 在ANSI C标准C的任何一种实现里面都会包含两个不同的环境 1 翻译环境会将源代码转变为可执行的二进制的机器指令。 2 运行环境用于执行实际的代码。 两个环境之间的关系可以用图片来描述一下 用语言简单改过一下就是各种源文件通过翻译环境中的编译和链接两步会将我们写的C代码转变成机器能够执行的二进制指令然后生成可执行程序.exe为后缀的文件在运行环境中就可以运行了。 所以可执行程序中存放的都是二进制指令如果直接用记事本打开会看到是一堆乱码 2 翻译环境 翻译环境主要由编译和链接两步组成而编译又由预处理预编译、编译、汇编三个过程所以翻译环境也可以说成是由预处理、编译、汇编、链接四小部分组成。 一个C语言的项目一般会由多个源文件共同构成每一个源文件会先经过编译器在vs中为cl.exe生成对应的目标文件Windows环境下后缀为.objLinux环境下后缀为.o然后在经过链接器vs中为link.exe把各个目标文件和链接库运行时库支持程序运行的基本函数的集合和第三方库一起链接为可执行程序。 1 预处理预编译 在预处理阶段源文件和头文件会被处理成为后缀为 .i 的文件。 预处理阶段主要会处理一些以 # 开头的预处理指令具体规则如下
预处理预编译处理规则 1将所有的 #define 删除并展开所有的宏定义2处理所有的预编译指令如 #if 、#endif、#ifdef、#elif、#else 等等3处理 #include 预编译指令将包含的头文件的内容插入到预编译指令的位置4删除所有注释5添加行号和文件名标识方便后续编译器生成调试信息等6 保留所有的 #pragma 的编译器指令编译器后续使用 第6条我们曾在结构体内存对齐中曾经使用过如
//修改默认对齐数为1
#pragma pack(1)//还原为默认对齐数
#pragma pack()
2 编译 编译的主要作用将C语言代码转变为汇编代码会对文件进行一系列的词法分析语法分析语义分析及优化生成相应的汇编代码文件。 如以下的这个代码
arr[index] (index 5) * (3 * 8);
1 词法分析 将原代码程序输入扫描器然后由扫描器进行词法分析将代码中的字符分割成一系列的记号关键词标识符特殊字符等。上述代码会在扫描器中会分割成以下记号
2 语法分析 经过词法分析之后语法分析器会对产生的记号进行语法分析产生语法树以表达式为结点的树
3 语义分析 接下来就是由语义分析器来完成语义分析即对表达式进行语法层面分析。编译器的分析是语义分析的静态分析通常包括声明和类型的匹配类型转换等等。在这个阶段会报告错误的语法信息。 语义标识后的语法树如图所示
3 汇编 经过了预处理和编译之后就到了汇编阶段在该阶段会用汇编器将汇编代码转变为机器执行的指令也就是二进制指令不做指令优化。 经过了以上三个阶段就生成了目标文件。
4 链接 链接会将一堆文件链接在一起生成一个可执行程序。 链接过程主要包括地址和空间分配符号决议和重定位等。其实链接解决的是一个项目中多文件、多模块之间的相互调用问题。
比如
//add.c
int Add(int x, int y)
{return x y;
}//test.c
#includestdio.h//extern声明外部函数
extern int Add(int, int);int main()
{int a Add(3, 5);printf(%d\n, a);return 0;
}
在add.c源文件中有一个Add函数。而在test.c源文件中用extern关键字声明了外部函数并在main函数中使用了Add函数。 其实在链接的过程中每一个源文件中分别会对函数和全局变量来生成一个符号表这个符号表里面存储了函数和全局变量的地址对于定义了的函数和全局变量会在符号表中存储真正的地址而对于文件中没有定义只是声明的函数和全局变量的地址会先在符号表中存储一个无效的地址然后链接的时候会将符号表合并在其他模块里面寻找真正定义过相同函数和全局变量的地址来修正文件中引用到该函数的地址若符号表中仍存在无效地址咋会报链接错误。 比如在以上例子中假设test.c 生成的符号表为
test.c 文件的符号表 Add函数0x00000000无效地址main函数0x1187f3c0 add.c 文件的符号表 Add函数0x12f2d7c4 在链接时会将两个符号表合并生成一个新的符号表
合成的符号表 Add函数0x12f2d7c4main函数0x1187f3c0 再比如以下这个例子
//add.c
int add(int x, int y)
{return x y;
}//test.c
#includestdio.hextern int Add(int x, int y);int main()
{int ret Add(3, 5);return 0;
}
test.c 文件的符号表 Add函数0x00000000无效地址main函数0x1187f3c0 add.c 文件的符号表 add函数0x12f2d7c4 合成的符号表 Add函数0x00000000无效地址main函数0x1187f3c0add函数0x12f2d7c4 合成的符号表中出现了无效地址所以运行时会报链接错误