宠物用品销售网站建设和技术现状建网站赚钱
🌺在本次的博客当中我们可以一起来学习一下C语言的编译链接以及预处理的知识。
🌺要谈到C语言的编译和链接就肯定得从我们C语言程序运行的时候所产生的诸多的文件类型了。我们正常情况下编辑书写代码的文件为 .c 文件,我们的 .c 文件通过遍历和链接步骤之后生成可以执行的 .exe 文件。我们的编译和链接步骤当中的编译步骤又可以细分为预编译,编译,汇编三个步骤。我们可以通过一张图片进行感受编译和链接的用法。
🌺在编译的步骤当中我们的.c文件首先会经过预处理生成一个.i文件,之后我们的.i文件又会经过编译生成我们的.s文件,最后我们的.s文件通过汇编操作最终生成一个.o文件。到此为止我们的编译的步骤才算到此为止。由于我们在编译的过程中生成的一系列文件不同,所以我们每个阶段所进行的操作也各不相同。首先在我们的预编译阶段主要会进行如下三个操作:1.#include 头文件的包含 2.#define 定义符号的替换和删除 3.注释的删除 在预编译阶段我们进行的都是文本的替换操作 并不会对代码进行过多的更改。
🌺下一个步骤叫做编译,在这个步骤当中编译器将C语言代码翻译成了汇编代码。在这一步骤我们的编译器会对代码进行语法分析,词法分析,语义分析,以及符号汇总。在我们的符号汇总阶段汇总的符号会形成一张符号表。最后一个阶段就是我们的汇编的阶段。我们通常情况下我们在上一阶段转换完成的汇编代码翻译成二进制的指令。并存放在我们的目标文件当中。
🌺当我们的编译阶段完成之后就来到了我们的链接阶段了。在这一阶段我们会进行的操作分为两步:1.合并段表 2.符号表的合并和重定位 当我们完成之后就会产生一个 .exe 文件。我们不同的文件之间的函数的使用会在这一阶段进行相互连接。
🌺在完成上述的步骤之后我们程序的编译链接的步骤也就完成了。接下来我们在来认识一下我们的预处理操作:我们在上面的讲解当中说到过,我们头文件的包含,宏定义,注释的删除等操作都是在这一步骤当中完成的。我们介绍预处理操作的时候主要针对于我们的宏定义进行的。
🌺我们最常见的宏定义是使用我们的 #define 进行定义的。假如我们在我们的程序当中定义了一个宏常量,那么我们在程序进行预处理的时候会将我们的关键字直接替换成我们对应的值。举一个简单的代码进行理解:
🌺就像是我们所执行的代码一样。通过内部分析我们可以知道我们的程序会在预处理的阶段直接将我们的NUM 替换成为我们定义的常量的数字12。除了使用宏定义定义一个常量之外我们呢还可以使用宏定义定义一个表达式,利用宏定义的替换操作达到函数的表达效果。在举一个简单的例子: 🌺看到这里相信你肯定会觉得宏定义很方便对吧。确实,宏定义使用起来确实很方便,但是我们使用宏定义的时候得格外小心。因为我们的宏定义不会进行语法的检查,只会进行无脑的替换,即使我们在宏定义当中的语法是错误的我们的程序也会直接替换到正式的代码当中,对编写代码造成困扰。还有就是我们对于复杂的宏定义的替换难以估计其输出的值。举一个简单的例子:
🌺按照我们正常的代码的编写的逻辑我们上面的代码应该可以正常的运行但是实际上却不是,这是因为我们在if当中使用了宏定义替换,而我们的宏定义替换已经定义了一个分号,而我们在if语句当中又重新加入了一个分号,使得我们下面的if无法进行特定的匹配。我们可以试着将我们宏定义当中的分号去掉,或者给我们if语句当中的语句加一个括号进行一下检验。 🌺上面仅仅是编译的错误,较多的使用宏定义可能还会造成无法预知的错误,比如下图的代码:
🌺按照我们正常的逻辑来分析代码:我们的ADD宏定义中应该得到的结果为8,之后乘3得到的结果应该是24。但是我们实际得到的结果却是18,这是因为我们的宏定义并不会主动给我们的定义内容加上括号。所以我们程序替换之后应进行的计算为:3+5*3 会计算3*5得到15 之后加上3。得到我们最终的答案18。要想解决上面的问题我们可以增加括号。更改之后的代码如下:
🌺 我们使用宏定义还可能会产生对后面的代码产生副作用。所谓的副作用指的是我们在使用 ++ 自增的语句的时候对本身变量产生的影响。
🌺看到上面的代码可能大家会很疑惑,为什么会由上面的结果呢?我们就来进行分析:首先我们调用宏定义的时候传入的参数为x++,y++。第一次使用的时候将我们的x更改为6,y更改为9,之后我们就会继续调用(b)的步骤,输出9。我们的y又会自增一变成10。所以就产生了上图所示的代码的运行结果。如果我们使用函数进行定义这一项功能的话就不需要考虑的这么复杂。 🌺我们的自增操作只会在函数的调用的时候进行一次,并不会在函数当中带入a++和b++而是带入一个值,这样就是我们代码的运行逻辑更加清晰。你可能会想:那什么时候使用宏定义什么时候使用函数呢?
🌺当我们的语句比较短的时候我们就可以使用我们的宏定义进行操作。因为我们的宏定义代码量比较短,我们使用函数的话,还会进行函数栈帧的创建。在我们创建函数所花费的时间要远远长于我们代码运行计算的时间。所以对于我们逻辑比较清晰,代码量比较短的代码来说我们使用宏定义程序运行的速度要快于我们的函数。我们可以通过这一特点进行宏定义和函数的选择。
🌺接下来我们再来认识一下我们的条件编译的内容。当我们在编写程序的时候在前面定义了一个宏定义之后我们之后的所有的操作都会自动进行宏替换,但是假如我们在使用完之后不想再让这个宏定义发挥功能了应该怎么办呢?这时候我们可以使用 #undef 命令进行宏定义的取消。沃尔玛呢仅需要将我们之前定义好的宏常量在我们的undef当中重新定义一遍即可。执行代码如下:
🌺 对于我们最常见的条件编译主要为 #if...#elif...#else...#endif或者 #if defined。在这些条件编译当中我们可以进行代码的选择性编译,针对我们的宏定义是否进行定义而控制我们的编译的内容。举一个简单的例子来说:
🌺我们所要在选择编译当中所要进行的操作结构如上图(上图中的为便于理解的伪代码,我们可以根据伪代码进行实际代码的完善)我们实际的代码的编写如下:
🌺但我们在前面定义了一个宏常量的话就可以对于这个宏常量进行判断,并输出特定的代码。其结构和我们的条件分支语句的思想大致相同。同样的我们也可以使用我们的 #if defined进行判断。我们的 #if defiend 的作用同样是对于我们之前定义的变量进行判断。如果定义了的话就执行相应的指令,没有的话就直接跳过。所示的代码如下:
🌺就像是我们上面所展示的示例效果那样。
🌺那么此上就是本次博客的全部内容了,感谢您的观看。