1、2019/5/15,华中科技大学计算机学院,1,C语言程序设计,The C Programming Language,华中科技大学计算机学院 曹计昌,2019/5/15,华中科技大学计算机学院,2,第6章 编译预处理,编译预处理:对源程序进行编译之前所作的工作,它由预处理程序负责完成。编译时,系统将自动引用预处理程序对源程序中的预处理指令进行处理。 程序员通过编译预处理命令规定编译器在编译前所作的工作。 预处理指令:以“#”号开始的指令。,2019/5/15,华中科技大学计算机学院,3,6.1 文件包含#include,功能:用指定文件的内容取代该预处理指令行,有2种形式: (1) #incl
2、ude 在指定的标准目录下寻找被包含文件 (2) #include “文件名“ 首先在用户当前目录中寻找被包含文件,若找不到,再在指定的标准目录下寻找 如:#include “stdio.h”#include “math.h”,2019/5/15,华中科技大学计算机学院,4,6.2 宏定义#define,功能:用一个标识符来表示一个字符串.一般形式为:#define 标识符 字符串 宏名:被定义的标识符。宏代换(宏展开):在编译预处理时,用字符串去取代宏名,预处理前 #define M (y*y+3*y ) void main(void) int s,y;printf(“Input a num
3、ber: “); scanf(“%d“, ,预处理后 void main(void) int s,y;printf(“Input a number: “); scanf(“%d“, ,2019/5/15,华中科技大学计算机学院,5,6.3 带参数的宏定义,#define 标识符(标识符,标识符,标识符) 字符串,宏调用:给出实参 宏展开:(1)用字符串替换宏,(2)用实参去替换形参,2019/5/15,华中科技大学计算机学院,6,例 定义计算x2的宏,#define SQ(x) (x)*(x) X:形式参数 宏调用:SQ(a+1) /*a+1为实参*/ 宏展开: (a+1) * (a+1)实际
4、上是用(x)*(x)代替SQ(x),用实参a+1代替形参X。 宏调用:SQ(SQ(a) 宏展开:( (a)*(a) * (a)*(a) ),2019/5/15,华中科技大学计算机学院,7,为什么要这么多的括号?,考虑 :#define SQ(x) x*x 宏调用: SQ(a+b) 宏展开:a+b*a+b /* 与(a+b)*(a+b)不同 */ 再考虑 :#define SQ(x) (x)*(x) 宏调用: 27/SQ(3) 宏展开:27/(3)*(3) /* 值27, 与 27/32 不同 */ 定义带参数的宏时,为了保证计算次序的正确性,表达式中的每个参数用括号括起来,整个表达式也用括号括
5、起来。,2019/5/15,华中科技大学计算机学院,8,注意:宏名和与左括号之间不能有空格,#define SQ (x) (x)*(x) 被认为是无参宏定义 。 宏调用:SQ(3) 宏展开:(x) (x)*(x) (3) /*显然错误的*/,2019/5/15,华中科技大学计算机学院,9,带参的宏虽被认为不安全,但还是很有价值,#define SQ(x) (x)*(x) 宏调用:SQ(+a) 宏展开: (+a)*(+a) /*a加2次 如是函数调用,将不会有问题 */ 宏节省了函数调用的开销,程序运行速度更快,形式参数不分配内存单元,不必作类型说明。但是,宏展开后使源程序增长。 宏比较适合于经
6、常使用的简短表达式,以及小的可重复的代码段;当任务比较复杂,需要多行代码才能实现时,或者要求程序越小越好时,就应该使用函数。,2019/5/15,华中科技大学计算机学院,10,6.4 取消宏定义#undef,终止宏名的作用域,其形式为:#undef 标识符 何时使用#undef指令? 防止宏名的冲突 #include “everything.h“#undef SIZE /*everything.h中定义了SIZE,就取消它;否则该指令不起作用*/ #define SIZE 100 保证调用的是一个实际函数而不是宏 #undef getcharint getchar(void) ,2019/5/
7、15,华中科技大学计算机学院,11,6.5 条件编译,条件编译:在预处理中进行条件控制,根据所求条件的值有选择地包含不同的程序部分,因而产生不同的目标代码。 这对于程序的移植和调试是很有用的 。 条件编译指令三种形式,控制流与if-else语句类似。 见p172 表6.1,2019/5/15,华中科技大学计算机学院,12,例 利用R计算圆或正方形的面积,预处理前 #define R /* #undef R*/ void main(void) float c,r,s; printf (“input a number: “); scanf(“%f“, #endif ,预处理后 void main(
8、void) float c,r,s; printf (“input a number: ”); scanf(“%f”,&c); r=3.14159*c*c; printf(“%fn”,r); 生成的目标程序较短,2019/5/15,华中科技大学计算机学院,13,6.6 assert宏,在头文件assert.h中,用来测试表达式的值是否符合要求,其形式如下:assert(condition)如果condition值非0,程序继续执行下一个语句。如果condition值0,就输出错误信息,并通过调用实用库中的函数abort终止程序的执行。,2019/5/15,华中科技大学计算机学院,14,assert宏举例,assert (x=10); 如果x大于10,就会输出如下包含行号和文件名的错误信息并中断执行:Assertion failed:x= 0,file test.c,line 12对于大多数编译器来说,在头文件assert.h的assert宏定义中,如果定义了符号常量NDEBUG,其后的assert将被忽略。因此,如果不再需要assert,那么可把代码行#define NDEBUG插入到程序中,而无需手工删除assert。,2019/5/15,华中科技大学计算机学院,15,本章习题,6.1, 6.2, 6.3, 6.5, 6.8, 6.10, 6.11,