c语言基础入门--C语言的预处理
小职 2022-01-04 来源 : 阅读 373 评论 0

摘要:本篇主要介绍了c语言基础入门--C语言的预处理,通过具体的内容展现,希望对c语言开发的学习有一定的帮助。

本篇主要介绍了c语言基础入门--C语言的预处理,通过具体的内容展现,希望对c语言开发的学习有一定的帮助。

c语言基础入门--C语言的预处理

前言

编译一个C语言程序涉及很多步骤。其中第一个步骤被称为预处理。C语言的预处理器在源代码编译之前对其进行一些文本性质的操作。它的主要任务包括删除注释、插入被#include指令包含的文件内容、定义和替换由#define指令定义的符号,同时确定代码的部分内容是否应该根据一些条件编译指令进行编译。


一、预定义符号

下表为C语言预处理器定义的符号。他们的值有的是字符串常量,有的是十进制数字常量。

c语言基础入门--C语言的预处理


二、#define

我们先来看一下它的用法


#define name stuff


有了这条指令以后,每当有符号name出现在这条指令之后时,预处理器就会把它替换为stuff。


如果定义中的stuff非常长,那就可以将它分成几行,除了最后一行,每行的末尾都要加一个反斜杠,如下面例子所示:


#define DEBUG_PRINT printf("File %s line %d:" \

"x = %d, y = %d, z = %d, \

__FILE__, __LINE__, x, y, z)


这里利用了一个特性:相邻的字符串常量被自动连接为一个字符串。在调试一个存在许多涉及一组变量的不同计算过程的程序时,这种类型的声明非常有用。我们可以很容易的插入一条测试语句,打印出它们的当前值。


x *= 2;

y += x;

z = x * y;

DEBUG_PRINT;


1.宏

#define机制包括一个规定,允许把参数替换到文本中,这种实现通常称为宏(macro)或者定义宏(define macro)。下面是宏的声明方式:


#define name(parameter-list) stuff


其中,parameter-list(参数列表)是一个由逗号分隔的符号列表,它们可能出现在stuff 中。参数列表的左括号必须与name紧邻。否则,参数列表就会被解释为stuf的一部分。


当宏被调用时,名字后面是一个由逗号分隔的值的列表,每个值都与宏定义中的一个参数相对应。当参数出现在程序中时,与每个参数对应的实际值都将会被替换到stuff中。例如:


#define SQUARE(x) x*x


SQUARE(5)


当这两条语句位于程序中时,预处理器就会用上面的表达式替换下面的表达式,就会变成:5 * 5。


但是上面这个宏存在一个问题,请大家观察下面的代码:


a = 5;

printf("%d\n", SQUARE(a + 1));


可能我们直观的觉得这段代码将打印36这个值。但是事实上,它会打印11。Why?来,我们按照宏的规则做一个替换,这条语句将变成:


printf("%d\n", a + 1 * a + 1);


发现问题了吗,这里由替换而产生的表达式并没有按照预想的次序进行求值。所以,我们要对宏定义的参数加上括号,包括stuff整体。这样就能产生我们预期的结果了。


在程序中扩展#define定义符号和宏时,需要涉及几个步骤。


1.在调用宏时,首先对参数进行检查,看看是否包含任何由#define定义的符号。如果存在,它们会首先被替换掉。


2.替换文本随后被插入到程序中原来文本的位置。对于宏,参数名被它们的值所替代。


3.最后,才气对结果文本进行扫描,看看它是否包含了任何由#define定义的符号。如果是的话,就重复上述处理过程。


2.宏与函数

宏非常频繁的用于执行简单的计算,比如在两个表达式中寻找其中较大的一个(或较小):


#define MAX(a, b) ((a) > (b) ? (a) : (b))


这个功能好像我们也能用函数来实现,那为什么不使用函数呢?有两个原因。首先,用于调用和从函数返回的代码很可能比实际执行这个小型工作的代码更大,所以使用宏要比使用函数在程序中的规模和速度都更胜一筹。


其次,更为重要的是,函数的参数必须声明为一种特定的类型,所以它只能在类型合适的表达式中使用。但是宏是与类型无关的。


有优点就会有缺点,和使用函数相比,使用宏的不利之处在于每次使用宏时,一份宏定义代码的副本都将会插入到程序中。除非宏非常短,否则使用宏可能会大幅度增加程序的长度。


还有一些任务无法用函数来实现,比如下面这个代码:


#define MALLOC(n, type) ((type*) malloc ((n) * sizeof(type)))


type是一个数据类型,而函数是无法将类型作为参数传递的。


3.带副作用的宏参数

当宏参数在宏定义中出现的次数吵过一次时,如果这个参数具有副作用,那么在使用这个宏时就可能出现危险,导致不可预料的后果。副作用就是在表达式求值时出现永久性的后果。如下:


x + 1;


这个表达式无论执行几百次都是一样的,所以它没有副作用。


x++;


但是这个表达式就不同了,每次执行都会改变x的值,每一次执行都是一个不同的结果。所以,这个表达式是具有副作用的。我们看下面的例子,你觉得它会打印出什么:


#define MAX(a, b) ((a) > (b) ? (a) : (b))


x = 5;

y = 8;

z = MAX(x++, y++);

printf("x =%d, y = %d, z = %d\n", x, y, z);


其结果是: x = 6, y = 10, z = 9。产生这个结果的原因是那个较小的值只增加了一次,而那个较大的值却增加了两次——第一次是在比较的时候,第二次是在执行?后面的表达式时。这就是一个具有副作用的宏参数,我们在使用的时候一定要注意。


4. 宏和函数的不同

通过一个表格来分析:

c语言基础入门--C语言的预处理


5.#undef

#undef这个预处理指令用于移除一个宏定义:


#undef name


如果现存的名字需要被重新定义,那么首先必须用#undef移除它的旧定义。


三、条件编译

在编译一个程序时,如果可以翻译或忽略选定的某条语句或某组语句,会给我们带来极大的便利。而条件编译(conditional compilation)可以实现这个目的。使用条件编译,可以选择代码的一部分是被正常编译还是完全忽略。用于支持条件编译的基本结构是#if指令和#endif指令。一下是其使用方式:


#if constant-expression

statements

#endif


当常量表达式(constant-expression)的值是非零,那么statements部分就被正常编译,否则预处理器就静默的删除它们。


同时,条件编译的另一个用途是在编译时选择不同的代码部分。所以#if指令还具有可选的#elif和#else子句。如下:


#if constant-expression

statements

#elif constant-expression

other statements

#else

other statements

#endif


#elif子句出现的次数可以不限。但是每个常量表达式(constant-expression)只有当前面所有常量表达式的值都为假时才会被编译。#else子句中的语句只有当前面所有常量表达式的值都为假时才会被编译。


四、文件包含

#include指令使另一个文件的内容被编译,就好像它实际出现在#include指令出现的位置一样。这种替换执行的方式很简单:预处理器删除这条指令,并用包含文件的内容取而代之。如果一个头文件被包含到十个源文件中,那它实际上被编译了十次。


1.函数库文件包含

C语言编译器支持两种不同类型的#include文件包含:函数库文件和本地文件。事实上,他们之间的区别很小。

函数库文件的包含使用以下语法:


#include<filename>


对于filename(文件名),并没有任何的限制,不过根据规定,标准库文件以一个.h后缀结尾。


编译器通过观察由编译器定义的“一系列标准位置”查找函数库头文件。你所使用的编译器文档会说明这些标准的位置是什么,以及怎样修改它们或者在列表中添加其他位置。


2.本地文件包含

以下是#include指令的另外一种形式:


#include"filename"


标准允许编译器自行决定是否把本地形式的#include和函数库形式的#include区别对待。可以先对本地头文件使用一种特殊的处理方式,如果失败,编译器再按照函数库头文件的处理方式对待它们进行处理。


处理本地头文件的一种常见策略就是在源文件所在的当前目录进行查找,如果该头文件并未找到,编译器就像查找函数库头文件一样在标准位置查找本地头文件。


总结

不要在一个宏定义的末尾加上分号,使其成为一条完整语句。在宏定义中使用参数,不要忘了在其周围加上括号。同时不要忘了在宏定义两边加上括号.


208小时视频教程,995份干货资料,领取资料+高薪就业咨询V:z_zhizuobiao

c语言基础入门--C语言的预处理


本文由 @小职 发布于职坐标。未经许可,禁止转载。
喜欢 | 0 不喜欢 | 0
看完这篇文章有何感觉?已经有0人表态,0%的人喜欢 快给朋友分享吧~
评论(0)
后参与评论

您输入的评论内容中包含违禁敏感词

我知道了

助您圆梦职场 匹配合适岗位
验证码手机号,获得海同独家IT培训资料
选择就业方向:
人工智能物联网
大数据开发/分析
人工智能Python
Java全栈开发
WEB前端+H5

请输入正确的手机号码

请输入正确的验证码

获取验证码

您今天的短信下发次数太多了,明天再试试吧!

提交

我们会在第一时间安排职业规划师联系您!

您也可以联系我们的职业规划师咨询:

小职老师的微信号:z_zhizuobiao
小职老师的微信号:z_zhizuobiao

版权所有 职坐标-一站式IT培训就业服务领导者 沪ICP备13042190号-4
上海海同信息科技有限公司 Copyright ©2015 www.zhizuobiao.com,All Rights Reserved.
 沪公网安备 31011502005948号    ICP许可  沪B2-20190160

©2015 www.zhizuobiao.com All Rights Reserved

208小时内训课程