第09章C语言高级程序设计.ppt
《第09章C语言高级程序设计.ppt》由会员分享,可在线阅读,更多相关《第09章C语言高级程序设计.ppt(83页珍藏版)》请在三一文库上搜索。
1、C语言程序设计,湖南工学院,第9章 C语言高级程序设计,9.1 编译预处理命令 9.2 位运算 9.3 结构体高级应用链表 本章小结,9.1 编译预处理命令,ANSI C 标准规定可以在C源程序中加入一些“预处理命令”,以改进程序环境,提高编程效率。C程序中编译预处理语句的作用不是实现程序的功能,它们是发送给编译系统的信息。也就是说,对于预处理命令,必须在程序编译之前,先对这些特殊命令进行“预处理”。经过预处理后程序不再包含预处理命令了。 C语言提供的预处理功能主要有宏定义、文件包含及条件编译三种。 分别用宏定义命令,文件包含命令,条件编译命令来实现。为了与一般C语句相区别,这些命令以符号“#
2、”开头。,9.1.1 宏,宏定义功能是定义符号常量和常参数的宏,宏定义编译预处理语句的格式如下 : #define 字符串1 字符串2 它把字符串1定义为字符串2,字符串1称为字符串2的宏定义,例如,下面是符号常量的宏定义: #define ON 1 #define OFF 0 它把符号常量ON定义为1,OFF定义为0。符号常量经过宏定义后,就可以在程序中作为常量使用。例如: if(a=ON) printf(“Switch is ONn”); else if(a=OFF) printf(“Switch is OFFn”);,在系统执行编译预处理过程时,将把程序中出现的字符串1一律用字符串2置换
3、,就是说程序中的符号常量用定义它们的常量置换,然后再对置换处理后的源文件进行编译。如上面程序段经编译预处理后成为下列形式: if(a=1) printf(“Switch is ONn”); else if(a=2) printf(“Switch is OFFn”); 在宏定义语句中,可以使用已经定义过的符号常量定义新的符号常理。例如: #define WID 40 #define LEN (WID+20) 其中第二个宏定义中使用了第一个宏定义的符号常量WID。在执行编译预处理时,程序中出现的所有符号常量WID都将被40置换,所有的符号常量LEN,不带参数的宏定义: 用一个指定的标识符(即名字)
4、来代表一个字符串。 一般形式:#define 标识符 字符串 例:#define PI 3.14159 说明: (1)宏名一般习惯用大写字母表示。 (2)使用宏名代替一个字符串,可以减少程序中重复书写某些字符串的工作量。 (3)宏定义是用宏名代替一个字符串,也就是作简单的置换,不作正确性检查。 (4)宏定义不是C语句,不必在行末加分号. #define PI 3.14159; area=PI*r*r; 展开: area=3.14159;*r*r; 出现语法错误 (5)#define命令出现在程序中函数的外面,宏名的有效范围为定义命令之后到本源文件结束。 (6)可以用#undef命令终止宏定义的
5、作用域。 格式:#undef 宏名,(7)在进行宏定义时,可以引用已定义的宏名,可以层层置换。 #define R 3.0 #define PI 3.14159 #define L 2*PI*R #define S PI*R*R main( ) printf(“L=%fnS=%fn”,L,S); (8)对程序中用双括号括起来的字符串内的字符,即使与宏名相同,也不进行置换。 (9)宏定义是专门用于预处理命令,只作字符替换。,带参数的宏定义: 不是进行简单的字符串替换,还要进行对数替换。 一般形式: #define 宏名(参数表) 字符串 例:#define S(a,b) a*b area=S(3
6、,2) #define PI 3.14159 #define S(r) PI*r*r main() float a ,area; a=3.6; area=S(a); printf(“r=%fnarea=%fn”,a,area); ,说明: (1)对带参数的宏的展开只是将语句中的宏名后面括号内的实参字符串代替#define命令行中的形参。 area=S(a+b) 把实参a+b代替PI*r*r中的形参r,成为: area=PI*a+b*a+b 则 #define S(r) PI*( r)*( r),都将被(40+20)置换。例如,程序中的下列语句: area=LEN*WID; 在执行编译预处理时,
7、该语句将被置换成: area=(40+20)*40; 经运算后变量area的值是2400。从上面的置换过程可以看到,LEN定义时包围WID+20的圆括号是不可缺少的,若上面的宏定义时不使用圆括号: #define LEN WID+20 则上面的area赋值表达式在编译预处理后成为: area=40+20*40; 这时变量area的计算结果值是840,它并不是预定的计算结果。因此,在进行宏定义时,为了保证宏定义被置换后仍保持正确的运算顺序,经常在定义式中使用必要的圆括号包围定义的式子。,在C语言程序中,宏定义语句除了定义符号常量外,还经常用于定义带参数的宏,带参数的宏是在定义的宏定义中可以带有若
8、干参数。例如: #define MULT2(X) X*X 其中,MULT2(X)称为带参数的宏,X是它的形式参数。该宏定义把MULT2(X)定义为X*X。在此定义后,MULT2(X)就可以用在程序中代替定义它的运算表达式X*X。它的形式参数的使用特性类似于函数的形式参数。在程序中需要计算某个数的平方值时,可以使用这个已定义 的宏,例如: a=10; c=MULT2(a); 在进行编译预处理时,带参数的宏用它的定义置换,其中的形式参数用实际使用的实际参数置换。因此,上面的赋值表达式置换后的形式是: c=a*a;,其中定义式中的形式参数X被实际参数a置换,该运算表达式的结果是100。当程序中需要计
9、算某两个变量和的平方时,如果使用上面定义的带参数的宏的话,如下所示: w=6;v=4; c=MULT2(w+v); 进行编译预处理后,上面的赋值表达式置换后的形式是: c=w+v*w+v; 它的运算顺序与预定的顺序完全不同,计算结果是34。如果上面的宏定义改为下列形式: #define MULT2(X) (X)*(X) 上面的赋值表达式置换后就成为: c=(w+v)*(w+v); 它的运算结果就正确了。这里又一次看到在定义式中使用必要圆括号的重要性。,【例9.1】程序中的宏定义(计算球的体积) 程序如L9-1.c 该程序在编译预处理中,计算球体积的表达式语句: v=4*PI*MULT3(r)/
10、3;其中的两个宏定义PI和MULT3将分别由定义它们的常量和表达式进行置换,实际上参加编译的语句如下所示: v=4*3.1415926*(r)*(r)*(r)/3; 在程序设计时,经常把那些反复使用的运算表达式定义不带参数的宏,这样一方面使程序更加简洁,另一方面可以使运算的意义更加明显。下面再给出几个带参数宏的例子,它们都是使用三项条件表达式定义的。 #define min(x,y) (xy)?x:y) 求x和y中较大的一个。,#define abs(x) (x=0)?x:-x) 求x的绝对值。 #define sign(x) (x0)?1:(x1)? 1:0) 判断x的符号。 上面给出的宏定
11、义中,在定义式的运算表达式里都是单纯的形式。在实际应用时,应该根据需要加上保证运算顺序的圆括号。,9.1.2 文件包含,“文件包含”处理 所谓“文件包含”处理是指一个源文件可以将另外一个源文件的全部内容包含进来,即将另外的文件包含到本文件之中。C语言提供了#inctude命令用来实现“文件包含”的操作。其一般形式为 # include “文件名” 或 #include 注意: (1)一个include命令只能指定一个被包含文件,如果要包含几个文件,要用几个include命令。,(2)如果文件1包含文件2,而文件2中要用到文件3的内容,则可在文件1中用两个inctude命令分别包含文件2和文件3
12、,而且文件3应出现在文件2之前,即在filel.C中定义: #include “file3.h” #include “file2.h” 这样,fite1和file2都可以用file3的内容。在file2中不必再用#include 了。 (3)在一个被包含文件中又可以包含另一个被包含文件,即文件包含是可以嵌套的。,【例9.2】文件put.h使用TYPE命令显示的内容如put.h代码,它包含有各种输出格式的带参数宏的定义: 程序如L9-2.c 【例9.3】 有如下两个源文件: filel.h L9-3.c,9.1.3 条件编译,为了解决程序的可移植性问题,C语言提供了条件编译命令,它能使用一个源程
13、序在不同的编译环境下生成不同的目标文件,条件编译命令有以下几种: (1)# ifdef 标识符 程序段1 # else 程序段2 # endif 其功能是用来测试测试一个标识符(宏名)是否被定义,如果标识符已被定义,则在程序编译阶段只编译程序段1,否则编译程序段2。该命令形式的简化形式是没有#else部分,这时,若标识符未定义,则此命令中没有程序段被编译。,(2)# ifndef 标识符 程序段1 # else 程序段2 # endif 其功能是用来测试测试一个标识符(宏名)是否未曾被定义,如果标识符未被定义,则编译程序段1,否则编译程序段2。该命令形式的简化形式是没有#else部分,这时,若
14、标识符已定义,则此命令中没有程序段被编译。 (3)#if 表达式 程序段1 #else 程序段2 #endif,它的作用是当指定的表达式为真时就编译程序段1,否则编译程序段2。其中的表达式必须是整型常量表达式(不包含sizeof运算符、强制类型转换和枚举常量)。该命令形式的简化形式是没有#else部分,这时,若表达式为“假”,则此命令中没有程序段被编译。 (4)#if 表达式1 程序段1 #elif 表达式2 程序段2 #elif 表达式3 程序段3 #else 程序段n #endif,这里的#elif其含义是“else if”,该命令的功能是,如果常量表达式1的值为“真”,则编译程序段1,否
15、则如果常量表达式2的值为“真”,则编译程序段2,如果常量表达式的值都为“假”,则编译程序段n。也可以没有#else部分,这时,若所有表达式的值都为“假”,则此命令中没有程序段被编译。 (5) #if defined(宏名) 程序段1 #else 程序段2 #endif 该命令等价于#ifdef 标识符,但该命令可以判别多个宏名的定义情况,而#ifdef命令只能判断一个宏名的定义情况。! Defined(宏名)的作用是当宏名未定义时其值为真,因此#if !defined(宏名)等价于#ifndef 标识符。,例如,在alloc.h文件中,可以看到以下关于NULL的定义,其目的是适合不同编译模式的
16、兼容性。 #ifndef NULL #if defined(_TINY_)|defined(_SMALL_)|defined(_MEDIUM_) #define NULL 0 #else #define NULL 0L #endif #endif 【例9.4】输入一个口令,根据需要设置条件编译,使之在调试程序时,按原码输出;在使用时输出“*”号。,#define DEBUG void main(viod) char pass80;int=-1; printf(“n Pleasa Input Password:”); do i+; passi=getch(); #ifdef DEBUG putc
17、har(passi); #else putchar(*); #endif while(passi!=n); ,9.2 位运算,所谓位运算,是指对一个数据的某些二进制位进行的运算。每个二进制位只能存放1位二进制数“0”或者“1”。通常把组成一个数据的最右边的二进制位称作第0位,从右以此称为第1位,第二位,最左边一位称作最高位,如图91所示。,9.2.1 位运算和位运算符,C语言提供6种运算符,如表9-1所示。 说明: 反运算符“”是单目运算符,其余是双目运算符,即要求两侧各有一个运算量。 位运算的运算对象只能是整型或字符型数据,而不能是实型数据。,9.2.2 位运算符的使用,1 按位取反运算符“
18、” 按位取反运算为单目运算,它将运算对象的各位取反,即将1变0,0变1。例如O24是对八进制数24(即二进制数00010100)按位求反: 00010100 11101011 运算结果为八进制数353。 2 按位与运算符“&” “按位与”是指两个运算对象按对应二进制位进行“逻辑与”运算,即当且仅当参加运算的两个对象的对应二进制都为1时,结果的对应二进制位为1,否则为0。即,0 因为x,y是整型,占两个字节,对应的二进制形式分别为0000000000000011和0000000000000101,所以 x = 0000000000000011 y = 0000000000000101 x&y =
19、 0000000000000001 因此,3&5的值值等于1。如果参加&运算的是负数(如-3& -5),则以补码形式表示为二进制数,然后按位进行“与”运算。 “按位与”运算的应用主要为:位清零、测试指定位的值和获取指定位的值。,(1)位清零 如果想将一个数据中的某些位清零,根据“按位与”运算的含义,只需要鼗这些位与0进行“按位与”运算即可。 例95 设有一个字符型变量(8位二进制位),把它的低4位清0。 分析:欲使x的低4位为0,只需将x的低4位与0进行“按位与”即可。即x=x&0xf0。运算过程如下: x= * * * * * * * * & 1 1 1 1 0 0 0 0 * * * *
20、0 0 0 0 其中“*”是1或0。,(2)测试指定位的值 要想判断某一指定位的值是1还是0,只需将这一位与1进行“按位与”运算,然后判断结果是否为0即可。 例96 设x是一个字符型变量(8位二进制位),判断x的最低位是否为0。 分析:想要判断x的最低位是0还是1,可以进行如下运算: if(x&0x01)!=0)则x的最低位是1; 或者 if(x&0x0x)=0)则x的最低位是0; 因为: x = * * * * * * * * & 0 0 0 0 0 0 0 1 0 0 0 0 0 0 0 *,所以若x最低位为1,则表达式结果为1;若最低位为0,则表达式结果为0。 注意:“按位与”运算&的优
21、先级低于关系运算符!=和=,所以表达式(x&0x01)的圆括号不能省略。 (3) 获取指定的值 要想获得某些位的值只需将这些位与1“按位与”运算,而将其他位清0即可。 例97 设X是unsigned类型的整数(16位二进制数),要想获取X的低8位,可做运算X&0X00ff。运算过程为: x=* * * * * * * * * * * * * * * * & 0 0 0 0 0 0 0 0 1 1 1 1 1 1 1 1 0 0 0 0 0 0 0 0 * * * * * * * * 可以看出,结果只保存了X的低8位。,例98 从键盘输入一个正整数,判断此数是奇数还是偶数。 分析:一个正数是奇数
22、还是偶数,只需要判断最低位是1还是0。若最低位为0,该数是偶数;最低位为1,则是奇数。 程序如L9-8.c 3 按位或运算符“|” “按位或”运算是指两个运算对象按对应二进制位进行“逻辑或”运算,即当参加运算的两个对象的对应二进制位有一个“1”时,结果的对应二进制位为“1”,如下所示: 0|0=0;0|1=1;1|0=1;1|1=1; 例如:设int x=3,y=-5,则x|y的结果如下:,x= 0000000000000011 | 1111111111111011 1111111111111011 “按位或“运算常用于对一个数据中的某些位置1。 例99 编一程序,从键盘输入一个字符,如果是大
23、写字母,则转换成小写字母输出。 分析:字符在计算机内是以ASC码表示的,小写字母的ASC码值范围为16进制数61H7AH,大写字母的ASC码值范围为16进制数41H5AH,即小写字母和大写字母的ASC码值相差20H。因此要想把大写字母转换成小写字母只需把大写字母的第6位置1即可。如:A的ASC码为41H,a的ASC码值为61H。 A=01000001 (41H) | 00100000 (20H) a=01100001 (61H),程序如L9-9.c 4 按位异或运算符“” “按位异或”运算是指两个运算对象按对应二进制位进行“逻辑异或”运算,即当参加运算的两个对象的相应二进制位一个为“0”,另一
24、个为“1”时,结果的对应二进制位为“1”,如下所示: 00=0;01=1;10=1;11=0; “按位异或”运算的应用; (1)使数据中的某些位求反。因为0和1的异或结果为1,1和1的“异或”结果为0,所以只需将要求反的位与1进行“异或”运算,就可以将该位求反。 (2)将变量清0。任何一个属于变量本身的“异或”结果都为0。利用“异或”运算的这个性质可以完成对变量的清0操作。,5 左移运算符“” 右移运算符“”的使用方式为: 运算对象右移位数 右移运算符将运算对象的每个二进制位同时向右移动指定的位数,从右边移出的低位部分被丢弃。对无符号数,左边空出的高位补0;对有符号数,正数的高位部分补0,负数
- 配套讲稿:
如PPT文件的首页显示word图标,表示该PPT已包含配套word讲稿。双击word图标可打开word文档。
- 特殊限制:
部分文档作品中含有的国旗、国徽等图片,仅作为作品整体效果示例展示,禁止商用。设计者仅对作品中独创性部分享有著作权。
- 关 键 词:
- 09 语言 高级 程序设计
链接地址:https://www.31doc.com/p-3417857.html