计算机的黑魔法

发布时间:2026/8/2 15:49:13
计算机的黑魔法 前言在这一章内容中我们会探究我们写的代码.c文件是如何变成一个可执行的程序.exe文件即我们主要讲解翻译环境只是粗略的讲解。翻译环境与执行环境这两个环境名字不同自然所实现的功能也就不一样先大致讲一下这两个环境分别有什么作用翻译是将我们写的test.c文件翻译成计算机能看懂的二进制指令也就是test.exe文件要想执行test.exe文件的话就需要执行环境更详细一点的那么接下来我们细说这两个环境看看每一个环境都是如何实现对应的功能的翻译环境编译和链接翻译包括两个大头编译和链接编译是使得每个test.c文件编译成目标文件.obj/.o文件链接是将每个目标文件加链接库通过链接器生成可执行程序.exe文件就像这样别看就只有这两个步骤其实还内藏玄机呢比如编译阶段就分为预编译编译汇编今儿我们就把这些操作掰开了揉碎了来讲给你。预编译test.i在这一章内容中我们会粗制的讲解一下预处理并不会太详细如果想详细的了解预处理方面的知识可以关注我的另一篇文章——预处理详解。在预处理阶段编译器会把test.c文件编译成test.i文件要是gcc编译器的话需要输入指令gcc test.c -E -o test.i就会生成一个test.i的文件我们就可以在test.i文件中看既然是粗制的讲解我们就直接说在编译的时候预处理要干哪些活1.将#include包含的头文件展开由上面两张图片我们可以看出在预处理阶段把包含的头文件都展开到test.i文件中2.删除注释由这张图片我们可以很直观的看到预处理阶段是如何处理注释操作的即直接删除更准确的说是用空格替代了3.宏替代我们用#define定义MAX为100即在预处理阶段中只要看到MAX就自动写为100由这三点我们可以看出预处理阶段进行的是文本操作关于预处理官方给的指导是预处理阶段主要处理那些源⽂件中#开始的预编译指令。。⽐如#include #define 处理的规则如下1.将所有的 #define 删除并展开所有的宏定义。2.处理所有的条件编译指令如 #if 、 #ifdef 、 #elif 、 #else 、 #endif 。3.处理#include预编译指令将包含的头⽂件的内容插⼊到该预编译指令的位置。这个过程是递归进行的也就是说被包含的头⽂件也可能包含其他⽂件。4.删除所有的注释5.添加⾏号和⽂件名标识⽅便后续编译器⽣成调试信息等。6.保留所有的 #pragma 的编译器指令编译器后续会使⽤。经过预处理后的 .i 文件中不再包含宏定义因为宏已经被展开。并且包含的头文件都被插⼊到 .i ⽂件中。所以当我们无法知道宏定义或者头⽂件是否包含正确的时候可以查看预处理后的 .i ⽂件编译test.s汇编代码编译是将源文件代码编译成汇编代码在gcc编译器上的命令行是gcc test.i -S -o test.s在编译成汇编代码的过程中要三步走1.词法分析 2.语法分析 3.语义分析比如我们在源代码写一个array[index] (index4)*(26);这个代码编译器要怎么编译呢第一个词法分析打标签将源代码程序被输⼊扫描器扫描器的任务就是简单的进⾏词法分析把代码中的字符分割成⼀系列 的记号关键字、标识符、字⾯量、特殊字符等。就像这样给每一个所写的代码打上标签。第二个语法分析看看有什么语法接下来语法分析器将对扫描产⽣的记号进⾏语法分析从⽽产⽣语法树。这些语法树是以表达式为 节点的树。第三个语义分析批改由语义分析器来完成语义分析即对表达式的语法层⾯分析。编译器所能做的分析是语义的静态分 析。静态语义分析通常包括声明和类型的匹配类型的转换等。这个阶段会报告错误的语法信息汇编test.obj二进制指令代码汇编器是将汇编代码转变成机器可执⾏的指令2进制的指令每⼀个汇编语句⼏乎都对应⼀条机器 指令。就是根据汇编指令和机器指令的对照表⼀⼀的进⾏翻译也不做指令优化。汇编操作是将test.s文件转换成test.obj文件在gcc编译器上是输入gcc test.s -c这串命令行达到的效果是由汇编代码变成一个二进制的代码机器可读链接test.exe对.obj文件进行合并和重定位我们先看看链接这个名字哈链接像不像连接那要是连接的话目标对象得是两个以上才行所以链接也有两个以上的目标文件。在编译阶段中我们对每一个.c文件编译成.obj文件但是每一个.obj文件都是单独存在的无法互通链接就是为了解决这个问题而诞生它的主要功能是解决的是⼀个项⽬中多⽂件、多模块之间互相调⽤的问题。链接的步骤地址和空间分配符号决议和重定位等这些步骤。我们看下面这个程序看看链接是如何发挥作用的这有两个.c文件我们在执行它的时候会产生两个.obj文件test.obj和Add.obj链接的对象就是这两个文件。链接的操作如下在链接的符号决议阶段中会扫描所有.obj文件的函数和变量这里变量就不展示了并生成对应的符号表而在重定位阶段中会把真实有意义的函数汇编成一个符号表。这是什么意思呢我们看上面的图片在test.c文件中应用外部函数Add的时候因为在test.c文件中本身没有Add函数会随便给一个地址在最后的重定位中所有有意义的函数地址重新组装成一个符号表。执行环境1.程序必须载⼊内存中。在有操作系统的环境中⼀般这个由操作系统完成。在独⽴的环境中程序的载⼊必须由手工安排也可能是通过可执⾏代码置⼊只读内存来完成。2.程序的执行便开始。接着便调用main函数。3.开始执⾏程序代码。这个时候程序将使⽤⼀个运⾏时堆栈stack存储函数的局部变量和返回 地址。程序同时也可以使⽤静态static内存存储于静态内存中的变量在程序的整个执⾏过程 ⼀直保留他们的值4.终⽌程序。正常终⽌main函数也有可能是意外终⽌。