![C语言课件:第14章 预处理器_第1页](http://file4.renrendoc.com/view/ce861792d4cb132a7a45eb865901a8ee/ce861792d4cb132a7a45eb865901a8ee1.gif)
![C语言课件:第14章 预处理器_第2页](http://file4.renrendoc.com/view/ce861792d4cb132a7a45eb865901a8ee/ce861792d4cb132a7a45eb865901a8ee2.gif)
![C语言课件:第14章 预处理器_第3页](http://file4.renrendoc.com/view/ce861792d4cb132a7a45eb865901a8ee/ce861792d4cb132a7a45eb865901a8ee3.gif)
![C语言课件:第14章 预处理器_第4页](http://file4.renrendoc.com/view/ce861792d4cb132a7a45eb865901a8ee/ce861792d4cb132a7a45eb865901a8ee4.gif)
![C语言课件:第14章 预处理器_第5页](http://file4.renrendoc.com/view/ce861792d4cb132a7a45eb865901a8ee/ce861792d4cb132a7a45eb865901a8ee5.gif)
版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领
文档简介
1、1第14章预处理器引言指令如#define 和#include 等是由预处理器进行处理的,而预处理器是一个在编译前编辑C程序的软件.C语言(和 C+语言)因为依赖预处理器而不同于其他的编程语言。预处理器是一个强大的工具,但它同时也可能是许多难以发现的错误的根源.214.1 预处理器的工作方式预处理器查找以#开头的预处理指令.#define 和#include指令.3预处理器的工作方式#define 指令定义一个宏( macro)即一个能够代表其它东西的名字,比如一个常量。预处理器会通过将宏的名字和它的定义存储在一起来响应#define指令当这个宏在后面的程序中使用到时,预处理器“扩展”了宏,将
2、宏替换为它所定义的值。4预处理器的工作方式#include指令告诉预处理器打开一个特定的文件,将它的内容作为正在编译的文件的一部分“包含”进来。例如,下面一行:#include 指示预处理器打开一个名字为 stdio.h的文件,并将它的内容加到当前的程序中。5预处理器的工作方式在编译过程中中,预处理器的角色如图所示:6预处理器的工作方式预处理器的输入是一个 C语言程序,程序可能包含指令。预处理器会执行这些指令,并在处理过程中删除这些指令。预处理器的输出被直接交给编译器。7预处理器的工作方式第二章中的 celsius.c :/* Converts a Fahrenheit temperature
3、 to Celsius */#include #define FREEZING_PT 32.0f#define SCALE_FACTOR (5.0f / 9.0f)int main(void) float fahrenheit, celsius; printf(Enter Fahrenheit temperature: ); scanf(%f, &fahrenheit); celsius = (fahrenheit - FREEZING_PT) * SCALE_FACTOR; printf(Celsius equivalent is: %.1fn, celsius); return 0;8预处
4、理器的工作方式预处理过后为:Blank lineBlank lineLines brought in from stdio.hBlank lineBlank lineBlank lineBlank lineint main(void) float fahrenheit, celsius; printf(Enter Fahrenheit temperature: ); scanf(%f, &fahrenheit); celsius = (fahrenheit - 32.0f) * (5.0f / 9.0f); printf(Celsius equivalent is: %.1fn, celsiu
5、s); return 0;9预处理器的工作方式预处理器不仅仅是执行了指令,还做了一些其他的事情。特别值得注意的是,它将每一处注释都替换为一个空格字符。有一些预处理器还会进一步删除不必要的空白字符,包括在每一行开始用于缩进的空格符和制表符。10预处理器的工作方式注意:预处理器仅知道少量 C语言的规则,在执行指令时非常有可能产生非法的程序。对于较复杂的程序,检查预处理器的输出可能是找到这类错误的有效途径。 1114.2 预处理指令大多数预处理指令属于下面 3种类型之一:宏定义。#define指令定义一个宏, #undef指令删除一个宏定义。 文件包含。 #include指令导致一个指定文件的内容被
6、包含到程序中。 条件编译。 #if、#ifdef、#ifndef、#elif、#else和#endif指令可以根据测试的条件来将一段文本块包含到程序中或排除在程序之外。12预处理指令几条应用于所有指令规则:指令都以#开始。#符号不需要在一行的行首,只要它之前只有空白字符就行。在 #后是指令名,接着是指令所需要的其他信息。 在指令的符号之间可以插入任意数量的空格或横向制表符。例如,下面的指令是合法的: # define N 100 13预处理指令指令总是在第一个换行符处结束,除非明确地指明要继续。如果想在下一行继续指令,必须在当前行的末尾使用字符,例如:#define DISK_CAPACITY
7、 (SIDES * TRACKS_PER_SIDE * SECTORS_PER_TRACK * BYTES_PER_SECTOR)14预处理指令指令可以出现在程序中任何地方通常将 #define和#include指令放在文件的开始,其他指令则放在后面,甚至在函数定义的中间。注释可以与指令放在同一行。实际上,在一个宏定义的后面加一个注释来解释宏的意义是一种比较好的习惯:#define FREEZING_PT 32.0f /* freezing point of water */1514.3 宏定义简单的宏,没有参数。带参数的宏。16简单的宏简单的宏定义有如下格式:#define identifi
8、er replacement-list #define 标识符 替代列表replacement-list是一系列的 C语言记号,包括标识符、关键字、数、字符常量、字符串字面量、运算符和标点符号。在文件后面的内容中,不管identifier在任何位置出现,预处理器都会用replacement-list代替它。17简单的宏不要在宏定义中放置任何额外的符号,否则它们会被作为替换列表的一部分。一种常见的错误是在宏定义中使用 = ,如:#define N = 100 /* WRONG */int aN; /* becomes int a= 100; */18简单的宏在宏定义的末尾使用分号结尾是另一个常见
9、错误,如:#define N 100; /* WRONG */int aN; /* becomes int a100; */19简单的宏简单的宏主要用来定义那些被称为“明示常量”的东西也就是数值、字符值和字符串值:#define STR_LEN 80#define TRUE 1#define FALSE 0#define PI 3.14159#define CR r#define EOS 0#define MEM_ERR Error: not enough memory20标识符通常用大写,以示区别!简单的宏使用#define来为常量命名的优点有:使程序更易于阅读。名字可以帮助读者理解常量的意
10、义。程序会更易于修改。我们只需要改变一个宏定义,就可以改变整个程序中出现的所有该常量的值。可以帮助避免前后不一致或键盘输入错误。假如数值常量3.14159在程序中大量出现,它可能被意外地误写为3.1416 或者3.14195。21带参数的宏带参数的宏定义有如下格式:#define 标识符(x1, x2, xn) 替换列表 其中 x1, x2, xn是标识符(宏的参数)。在宏的名字和左括号之间必须没有空格,否则会认为是在定义一个简单的宏,其中(x1, x2, xn)是替换列表的一部分。22带参数的宏当预处理器遇到一个带参数的宏,会将定义存储起来以便后面使用。在后面的程序中,如果任何地方出现了标识
11、符(y1, y2, yn)格式的宏调用(其中 y1, y2, yn是一系列标记),预处理器会使用替换列表替代,并使用 y1替换 x1,y2替换 x2,依此类推。带参数的宏经常用来作为一些简单的函数使用。23带参数的宏带参数的宏举例:#define MAX(x,y) (x)(y)?(x):(y)#define IS_EVEN(n) (n)%2=0)调用这些宏:i = MAX(j+k, m-n);if (IS_EVEN(i) i+;宏替换后:i = (j+k)(m-n)?(j+k):(m-n);if (i)%2=0) i+;24带参数的宏复杂的类似函数的宏:#define TOUPPER(c) (
12、a=(c)&(c)=z?(c)-a+A:(c) 头提供了一个更容易移植的相似函数toupper。带参数的宏也可能具有空的参数列表:#define getchar() getc(stdin)空的参数列表不是必需的,但可以使 getchar更像一个函数。25带参数的宏使用带参数的宏替代实际的函数有两个优点:程序可能会稍微快些。一个函数调用在执行时通常会有些额外开销 存储上下文信息、复制参数的值等。而一个宏的调用则没有这些运行开销。宏会更“通用”。与函数的参数不同,宏的参数没有类型。只要预处理后的程序依然是合法的,宏可以接受任何类型的参数。26带参数的宏带参数的宏也有一些缺点。编译后的代码通常会变大
13、。每一处宏调用都会导致插入宏的替换列表,由此导致程序的源代码增加 (因此编译后的代码变大)。当宏调用嵌套时,这个问题会相互叠加从而使程序更加复杂。n = MAX(i, MAX(j, k);预处理后的这条语句:n = (i)(j)(k)?(j):(k)?(i):(j)(k)?(j):(k);27带参数的宏带参数的宏也有一些缺点宏参数没有类型检查。无法用一个指针来指向一个宏。28带参数的宏宏可能会不止一次地计算它的参数。如果参数有副作用,多次计算参数的值可能会产生意外的结果。n = MAX(i+, j);这条语句在预处理之后的结果:n = (i+)(j)?(i+):(j);如果 i大于 j,那么
14、i可能会被(错误地)增加了两次,同时 n可能被赋予了错误的值。29为了自保护,最好避免使用带有副作用的参数。带参数的宏带参数的宏经常用作需要重复书写的代码段模式。显示整数的一种更容易的宏:#define PRINT_INT(n) printf(%dn, n)预处理器将把下行PRINT_INT(i/j);转换为printf(%dn, i/j);30#运算符宏定义可以包含两个运算符:#和#。#运算符将一个宏的参数转换为字符串字面量。它仅允许出现在带参数的宏的替换列表中。31#运算符改进后的 PRINT_INT:#define PRINT_INT(n) printf(#n = %dn, n)调用PR
15、INT_INT(i/j);将转换为printf(i/j = %dn, i/j);编译器自动连接两个相邻的字符串字面量,因此上面语句等价为:printf(i/j = %dn, i/j);32#运算符#运算符可以将两个记号(例如标识符)“粘”在一起,成为一个记号。如果其中一个操作数是宏参数,“粘合”会在当形式参数被相应的实际参数替换后发生。33#运算符使用# 运算符的宏:#define MK_ID(n) i#n下面声明调用 MK_ID 三次:int MK_ID(1), MK_ID(2), MK_ID(3);预处理过后,该语句为:int i1, i2, i3;34宏的通用属性同时适用于简单和带参数的
16、宏的规则:宏的替换列表可以包含对另一个宏的调用。例如:#define PI 3.14159#define TWO_PI (2*PI)当预处理器在后面的程序中遇到 TWO_PI时,会将它替换成(2*PI)。预处理器会不断重新检查替换列表,直到将所有的宏名字都替换掉为止。 35宏的通用属性预处理器只会替换完整的记号,而不会替换记号的片断。因此,预处理器会忽略嵌在标识符名、字符常量、字符串字面量之中的宏名。例如:#define SIZE 256int BUFFER_SIZE;if (BUFFER_SIZE SIZE) puts(Error: SIZE exceeded);预处理过后:int BUFF
17、ER_SIZE;if (BUFFER_SIZE 256) puts(Error: SIZE exceeded);36宏的通用属性一个宏定义的作用范围通常到出现这个宏的文件末尾。一个定义在函数中的宏并不是仅在函数内起作用,而是作用到文件末尾。宏不可以被定义两遍,除非新的定义与旧的定义是一样的。37宏的通用属性宏可以使用 #undef指令“取消定义”。#undef指令有如下形式: #undef 标识符 其中标识符是宏名#undef指令的一个用途是取消一个宏的现有定义,以便于重新给出新的定义。38宏定义中的圆括号宏定义中的替换列表往往需要圆括号,以免发生意料之外的结果。如果宏的替换列表中有运算符,那
18、么始终要将替换列表放在括号中:#define TWO_PI (2*3.14159)如果宏有参数,每次参数在替换列表中出现时都要放在圆括号中:#define SCALE(x) (x)*10)没有括号的话,编译器可能会不按我们期望的方式应用运算符的优先级和结合性规则。39宏定义中的圆括号考虑下面的宏定义,其中的替换列表没有添加圆括号: #define TWO_PI 2*3.14159/* needs parentheses around replacement list */预处理过程中,语句:conversion_factor = 360/TWO_PI;成为conversion_factor =
19、 360/2*3.14159;除法会在乘法之前完成,产生期望之外的结果。40宏定义中的圆括号当宏有参数时,仅给替换列表添加圆括号是不够的。参数的每一次出现都要添加圆括号。#define SCALE(x) (x*10) /* needs parentheses around x */在预处理过程中,语句j = SCALE(i+1);变为j = (i+1*10);由于乘法的优先级比加法高,这条语句等价于:j = i+10;4114.4 条件编译C语言的预处理器可以识别大量用于支持条件编译的指令。预处理器根据条件测试结果来决定包含或排除程序中的一些片断。42#if指令和#endif指令假如我们正在调
20、试一个程序。我们想要程序显示出特定变量的值,因此将 printf函数调用添加到程序中重要的部分。一旦找到错误,经常需要保留这些 printf函数调用,以备以后使用。条件编译允许我们保留这些调用,但是让编译器忽略它们。43#if指令和#endif指令首先定义一个宏,并给它一个非 0的值:#define DEBUG 1接下来,我们要在每组 printf函数调用的前后加上#if和#endif:#if DEBUGprintf(Value of i: %dn, i);printf(Value of j: %dn, j);#endif44#if指令和#endif指令在预处理过程中,# if指令会测试 DE
21、BUG的值。由于 DEBUG的值非0,因此预处理器会将这两个 printf函数调用保留在程序中(但#if和#endif行会消失)。如果将 DEBUG的值改为 0并重新编译程序,预处理器则会将这 4行代码都删除。可以将#if-#endif保留在最终的程序中,这样如果程序在运行时出错,可以继续产生这些诊断信息(将 DEBUG改为 1并重新编译)。45#if指令和#endif指令一般来说,#if指令的格式如下:#if constant-expression#endif当预处理器遇到#if指令时,会计算常量表达式。如果表达式的值为0,那么在# if与 #endif之间的行将在预处理过程中从程序中删除。
22、否则,这些在# if和#endif之间的行会被保留在程序中,并继续被编译器处理这时#if和#endif对程序没有任何影响。46#if指令和#endif指令对于没有定义过的标识符,#if指令会把它当作是值为 0的宏对待。如果我们没有定义 DEBUG, 则测试:#if DEBUG将会失败(但不会产生错误消息)。而测试:#if !DEBUG则会成功。47defined运算符预处理器支持三种运算符: #, #, 和defined。当defined应用于标识符时,如果标识符是一个当前定义过的宏则返回1,否则返回0。# defined运算符通常与#if指令结合使用。48defined运算符例子:#if d
23、efined(DEBUG)#endif仅当 DEBUG被定义成宏时,# if和#endif之间的代码会被保留在程序中。DEBUG两侧的括号不是必需的,因此可以简单写成:#if defined DEBUG也不必给 DEBUG 一个值:#define DEBUG49#ifdef指令和#ifndef指令#ifdef指令测试一个标识符是否已经定义为宏:#ifdef identifier其效果与下面语句相同:#if defined(identifier)#ifndef 指令测试一个标识符当前是否未被定义为宏:#ifndef identifier其效果与下面语句相同:#if !defined(identifier)50#ifdef指令
温馨提示
- 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
- 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
- 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
- 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
- 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
- 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
- 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。
最新文档
- 2025农村简易买卖合同
- 2025如何确定劳动合同的成立商业保理资格
- 最高额抵押担保合同
- 宣传栏维修合同范本
- 委托担保合同参考范文
- 采购检测体温设备合同范本
- 2025年新科版必修1历史上册月考试卷含答案
- 借款合同范本现金
- 苗木购销合同范文集锦
- 泥浆外运承包合同
- 2024年江西省南昌市中考一模数学试题(含答案)
- 48贵州省贵阳市2023-2024学年五年级上学期期末数学试卷
- 《采暖空调节能技术》课件
- CONSORT2010流程图(FlowDiagram)【模板】文档
- 游戏综合YY频道设计模板
- arcgis软件操作解析课件
- 中兴ZCTP 5GC高级工程师认证考试题库汇总(含答案)
- 大学生创新创业教程PPT全套完整教学课件
- 小学科学项目化作业的设计与实施研究
- 2020年中考生物试卷及答案
- MCNP-5A程序使用说明书
评论
0/150
提交评论