C语言高级程序设计.ppt

上传人:wuy****n92 文档编号:80458248 上传时间:2023-03-23 格式:PPT 页数:83 大小:439.50KB
返回 下载 相关 举报
C语言高级程序设计.ppt_第1页
第1页 / 共83页
C语言高级程序设计.ppt_第2页
第2页 / 共83页
点击查看更多>>
资源描述

《C语言高级程序设计.ppt》由会员分享,可在线阅读,更多相关《C语言高级程序设计.ppt(83页珍藏版)》请在得力文库 - 分享文档赚钱的网站上搜索。

1、C语言程序设计湖南工学院第第9 9章章 C C语言高级程序设计语言高级程序设计9.1 9.1 编译预处理命令编译预处理命令9.2 9.2 位运算位运算9.3 9.3 结构体高级应用链表结构体高级应用链表本章小结本章小结9.1 9.1 编译预处理命令编译预处理命令 ANSI C 标准规定可以在C源程序中加入一些“预处理命令”,以改进程序环境,提高编程效率。C程序中编译预处理语句的作用不是实现程序的功能,它们是发送给编译系统的信息。也就是说,对于预处理命令,必须在程序编译之前,先对这些特殊命令进行“预处理”。经过预处理后程序不再包含预处理命令了。C语言提供的预处理功能主要有宏定义、文件包含及条件编

2、译三种。分别用宏定义命令,文件包含命令,条件编译命令来实现。为了与一般C语句相区别,这些命令以符号“#”开头。9.1.1 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(“Switc

3、h is OFFn”);不带参数的宏定义:用一个指定的标识符(即名字)来代表一个字符串。一般形式:#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命令出现在程序中函数的外面,宏名的有效范围为定义命令之后到本源文件结束

4、。(6)可以用#undef命令终止宏定义的作用域。格式:#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

5、=S(3,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)9.1.2 9.1.2 文件包含文件包含 “文件包含”处理 所谓“文件包含”处理是指一个源文件可以将另外一个源文件的全部内容包含进来

6、,即将另外的文件包含到本文件之中。C语言提供了#inctude命令用来实现“文件包含”的操作。其一般形式为#include “文件名”或#include 注意:(1)一个include命令只能指定一个被包含文件,如果要包含几个文件,要用几个include命令。(2)如果文件1包含文件2,而文件2中要用到文件3的内容,则可在文件1中用两个inctude命令分别包含文件2和文件3,而且文件3应出现在文件2之前,即在filel.C中定义:#include “file3.h”#include “file2.h”这样,fite1和file2都可以用file3的内容。在file2中不必再用#include

7、 了。(3)在一个被包含文件中又可以包含另一个被包含文件,即文件包含是可以嵌套的。【例9.2】文件put.h使用TYPE命令显示的内容如put.h代码,它包含有各种输出格式的带参数宏的定义:程序如L9-2.c【例9.3】有如下两个源文件:filel.h L9-3.c9.1.3 条件编译条件编译 为了解决程序的可移植性问题,C语言提供了条件编译命令,它能使用一个源程序在不同的编译环境下生成不同的目标文件,条件编译命令有以下几种:(1)#ifdef 标识符 程序段1#else 程序段2#endif 其功能是用来测试测试一个标识符(宏名)是否被定义,如果标识符已被定义,则在程序编译阶段只编译程序段1

8、,否则编译程序段2。该命令形式的简化形式是没有#else部分,这时,若标识符未定义,则此命令中没有程序段被编译。(2)#ifndef 标识符 程序段1#else 程序段2#endif其功能是用来测试测试一个标识符(宏名)是否未曾被定义,如果标识符未被定义,则编译程序段1,否则编译程序段2。该命令形式的简化形式是没有#else部分,这时,若标识符已定义,则此命令中没有程序段被编译。(3)#if 表达式 程序段1#else 程序段2#endif 它的作用是当指定的表达式为真时就编译程序段1,否则编译程序段2。其中的表达式必须是整型常量表达式(不包含sizeof运算符、强制类型转换和枚举常量)。该命

9、令形式的简化形式是没有#else部分,这时,若表达式为“假”,则此命令中没有程序段被编译。(4)#if 表达式1 程序段1#elif 表达式2 程序段2#elif 表达式3 程序段3#else 程序段n#endif 这里的#elif其含义是“else if”,该命令的功能是,如果常量表达式1的值为“真”,则编译程序段1,否则如果常量表达式2的值为“真”,则编译程序段2,如果常量表达式的值都为“假”,则编译程序段n。也可以没有#else部分,这时,若所有表达式的值都为“假”,则此命令中没有程序段被编译。(5)#if defined(宏名)程序段1#else 程序段2#endif 该命令等价于#i

10、fdef 标识符,但该命令可以判别多个宏名的定义情况,而#ifdef命令只能判断一个宏名的定义情况。!Defined(宏名)的作用是当宏名未定义时其值为真,因此#if!defined(宏名)等价于#ifndef 标识符。例如,在alloc.h文件中,可以看到以下关于NULL的定义,其目的是适合不同编译模式的兼容性。#ifndef NULL#if defined(_TINY_)|defined(_SMALL_)|defined(_MEDIUM_)#define NULL 0#else#define NULL 0L#endif#endif【例9.4】输入一个口令,根据需要设置条件编译,使之在调试程

11、序时,按原码输出;在使用时输出“*”号。#define DEBUGvoid main(viod)char pass80;int=-1;printf(“n Pleasa Input Password:”);do i+;passi=getch();#ifdef DEBUGputchar(passi);#elseputchar(*);#endifwhile(passi!=n);9.2 9.2 位运算位运算 所谓位运算,是指对一个数据的某些二进制位进行的运算。每个二进制位只能存放1位二进制数“0”或者“1”。通常把组成一个数据的最右边的二进制位称作第0位,从右以此称为第1位,第二位,最左边一位称作最高

12、位,如图91所示。9.2.1 9.2.1 位运算和位运算符位运算和位运算符 C语言提供6种运算符,如表9-1所示。说明:反运算符“”是单目运算符,其余是双目运算符,即要求两侧各有一个运算量。位运算的运算对象只能是整型或字符型数据,而不能是实型数据。9.2.2 9.2.2 位运算符的使用位运算符的使用 1 按位取反运算符“”按位取反运算为单目运算,它将运算对象的各位取反,即将1变0,0变1。例如O24是对八进制数24(即二进制数00010100)按位求反:00010100 11101011运算结果为八进制数353。2 按位与运算符“&”“按位与”是指两个运算对象按对应二进制位进行“逻辑与”运算,

13、即当且仅当参加运算的两个对象的对应二进制都为1时,结果的对应二进制位为1,否则为0。即 0&0=0;0&1=0;1&0=0;1&1=1;例如:设int x=3,y=5;因为x,y是整型,占两个字节,对应的二进制形式分别为0000000000000011和0000000000000101,所以x=0000000000000011y=0000000000000101 x&y=0000000000000001 因此,3&5的值值等于1。如果参加&运算的是负数(如-3&-5),则以补码形式表示为二进制数,然后按位进行“与”运算。“按位与”运算的应用主要为:位清零、测试指定位的值和获取指定位的值。(1)

14、位清零 如果想将一个数据中的某些位清零,根据“按位与”运算的含义,只需要鼗这些位与0进行“按位与”运算即可。例95 设有一个字符型变量(8位二进制位),把它的低4位清0。分析:欲使x的低4位为0,只需将x的低4位与0进行“按位与”即可。即x=x&0 xf0。运算过程如下:x=*&1 1 1 1 0 0 0 0 *0 0 0 0 其中“*”是1或0。(2)测试指定位的值 要想判断某一指定位的值是1还是0,只需将这一位与1进行“按位与”运算,然后判断结果是否为0即可。例96 设x是一个字符型变量(8位二进制位),判断x的最低位是否为0。分析:想要判断x的最低位是0还是1,可以进行如下运算:if(x

15、&0 x01)!=0)则x的最低位是1;或者 if(x&0 x0 x)=0)则x的最低位是0;因为:x=*&0 0 0 0 0 0 0 1 0 0 0 0 0 0 0*所以若x最低位为1,则表达式结果为1;若最低位为0,则表达式结果为0。注意:“按位与”运算&的优先级低于关系运算符!=和=,所以表达式(x&0 x01)的圆括号不能省略。(3)获取指定的值要想获得某些位的值只需将这些位与1“按位与”运算,而将其他位清0即可。例97 设X是unsigned类型的整数(16位二进制数),要想获取X的低8位,可做运算X&0X00ff。运算过程为:x=*&0 0 0 0 0 0 0 0 1 1 1 1

16、1 1 1 1 0 0 0 0 0 0 0 0*可以看出,结果只保存了X的低8位。例98 从键盘输入一个正整数,判断此数是奇数还是偶数。分析:一个正数是奇数还是偶数,只需要判断最低位是1还是0。若最低位为0,该数是偶数;最低位为1,则是奇数。程序如L9-8.c3 按位或运算符“|”“按位或”运算是指两个运算对象按对应二进制位进行“逻辑或”运算,即当参加运算的两个对象的对应二进制位有一个“1”时,结果的对应二进制位为“1”,如下所示:0|0=0;0|1=1;1|0=1;1|1=1;例如:设int x=3,y=-5,则x|y的结果如下:x=0000000000000011 “按位或“运算常用于对一

17、个数据中的某些位置1。例99 编一程序,从键盘输入一个字符,如果是大写字母,则转换成小写字母输出。分析:字符在计算机内是以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.c4 按位异或运算符“”“按位异或”运算是指两个运算对象按对应二进制位进行“逻辑异或”运算,即

18、当参加运算的两个对象的相应二进制位一个为“0”,另一个为“1”时,结果的对应二进制位为“1”,如下所示:00=0;01=1;10=1;11=0;“按位异或”运算的应用;(1)使数据中的某些位求反。因为0和1的异或结果为1,1和1的“异或”结果为0,所以只需将要求反的位与1进行“异或”运算,就可以将该位求反。(2)将变量清0。任何一个属于变量本身的“异或”结果都为0。利用“异或”运算的这个性质可以完成对变量的清0操作。5 左移运算符“”左移运算符“”的使用方式为:运算对象”右移运算符“”的使用方式为:运算对象右移位数 右移运算符将运算对象的每个二进制位同时向右移动指定的位数,从右边移出的低位部分

19、被丢弃。对无符号数,左边空出的高位补0;对有符号数,正数的高位部分补0,负数的高位部分补0还是1和计算机系统有关。移入0的称为“逻辑右移”,移入1的称为“算术右移”。“逻辑右移”相当于无符号数除以2,“算术右移”相当于有符号数除以2。例如:7 位复合赋值运算符 类似于算术运算的复合运算符,位运算符和赋值运算符也可以构成“复合赋值运算符”。如9-2所示。8 位运算的应用(1)键盘扫描码 键盘上除了ASC|码外,还有非ASC|码(如左移键“”对应的编码),叫扩充键盘码。我们把扩充键盘码放在高八位,ASC|码放在低八位所组成的代码称为扫描码。对于某一特定的扫描码,若其低八位不为零,则此八位就是相应字

20、符的ASC|码值;若低八位是零,则高八位是扩充键盘码,需要再读取入第二个字节,根据它的值来判断它是那一个功能键。表9-3给出了单功能键和组合功能键的键值,表中代码 系指第二个字节键值的十进制数。例如,“”的扫描码低八位应为零,而高八位是0X4B,所以,“”键的扫描码为0X4B00;回车键也有对应的ASC|码,故其扫描码的低八位是回车键的ASC|码值0X0D。(2)测试键盘扫描码 调用标准库函数bioskey()读取键盘扫描码,再通过位运算分离低八位字符的ASC|码和高八位的扩充键盘码。注意使用bioskey()函数,应在头部加上#include“bios.h”。例910 利用bioskey()

21、函数,测试键盘扫描码,按ESC键退出。算法设计:(1)利用位的“与”运算,提取低8位low=key&0 x00ff;如果low不为零,则此8位就是相应字符的ASC|码值;否则(2)利用位的“与”和右移“”运算,提取高8位的扩充键盘码 hig=(key&0 xff00)8;程序如L9-10.c说明:(1)关于函数bioskey()函数bioskey()的功能是用于识别用户按键和获得按键值。函数原型是:int bioskey(int cmd);在中定义,在使用它时,应用include命令将bios.h文件包含进来。其中参数cmd可取0或1。当cmd=1时,检测键盘是否击键,如果没有击键,函数将返回

22、0,否则返回非零;当cmd=0时,返回从键盘输入的扫描码。语句key=bioskey(0);读取键盘输入的扫描码,并存储在变量key中。9.2.3 位段位段 在计算机中一般以字节为单位存放数据,但实际上有时存储一个信息不必用一个字节或多个字节,例如,“真”或“假”用0或1表示,即只需要1位表示即可。因此,在计算机里常常在一个字节中放几个信息。C语言提供两种方法,操作一个字节中的一个或几个二进制位。1 位运算法 如图9-2所示,假设a、b、c和d分别占2位、6位、4位和4位,设data由a、b、c和d组成。如果想将c的值置为12,用位运算方法,操作如下:将数x=12左移4位,使1100成为右面的

23、第47位,即:x=4(0000000011000000)(2)将data与0 xff0f进行“与”运算,使data的第47位全为0,即:data&=0 xffof;(3)将data与“124”进行“按位或”运算,即:data|=(124)或将3步结合起来,可以表示为:data=data&0 xff0f|(12str,pstr)!=0&temp-next!=NULL)/*若节点的字符串与输入字符串不同,并且未到链表尾*/p=temp;temp=temp-next;/*跟踪链表的增长,即指针后移*/if(strcmp(temp-str,pstr)=0)/*找到字符串*/if(temp=head)/

24、*表头节点*/printf(delete string:%sn,temp-str);head=head-next;free(temp);/*释放被删节点*/elsep-next=temp-next;/表*中节点*/printf(delete string:%sn,temp-str);free(temp);else printf(nno find string!n);没/找*到要删除的字符串*/return(head);/*返回表头指针*/2.链表的插入首先定义链表的结构:structint num;/*学生学号*/char str20;/*姓名*/struct node*next;在建立的单链

25、表中,插入节点有三种情况,如图9-7所示。插入的节点可以在表头、表中或表尾。假定我们按照以学号为顺序建立链表,则插入的节点依次与表中节点相比较,找到插入位置。由于插入的节点可能在链表的头,会对链表的头指针造成修改,所以定义插入节点的函数的返回值定义为返回结构体类型的指针。节点的插入函数如下:struct node*insert(head,pstr,n)/*插入学号为n、姓名为p s t r 的节点*/struct node*head;/*链表的头指针*/char*pstr;int n;struct node*p1,*p2,*p3;p1=(struct node*)malloc(sizeof(s

26、truct node)分;配/*一个新节点*/strcpy(p1-str,pstr);/*写入节点的姓名字串*/p1-num=n;/*学号*/p2=head;if(head=NULL)/*空表*/head=p1;p1-next=NULL;/*新节点插入表头*/else/*非空表*/while(np2-num&p2-next!=NULL)/*输入的学号小于节点的学号,并且未到表尾*/p3=p2;p2=p2-next;/*跟踪链表增长*/if(nnum)/*找到插入位置*/if(head=p2)/*插入位置在表头*/head=p1;p1-next=p2;else/*插入位置在表中*/p3-next

27、=p1;p1-next=p2;else/*插入位置在表尾*/p2-next=p1;p1-next=NULL;return(head);/*返回链表的头指针*/9.3.3 遍遍历链表历链表 由于链表是一个动态的数据结构,链表的各个结点由指针链接在起,访问链表元素时通过每个链表结点的指针逐个找到该结点的下一个结点,直找到链表尾,链表的最后一个结点的指针为空。例如:编历链表函数。void outputlist(LINKLIST*head)LINKLIST*currenthead-next;while(current!NULL)printf(%dn,current-info);current=curr

28、ent-next;return;双向链表双向链表 每个结点中只包括一个指向下个结点的指针域,这种链表称为单向链表。如果要在单向链表一个指针所指的当前位置插入一个新结点,就必须从链表头指针开始逐个遍历直到当前指针所指结点的前一结点,修改这个结点的指针。双向链表的每个结点中包括两个指针域,分别指向该结点的前一个结点和后一个结点。在双向链表中由任何一个结点都很容易找到其前面的结点和后面的结点,而不需要在上述的插入(及删除)操作中由头结点开始寻找。定义双向链表的结点结构为typedef struct node DATATYPE info;node*priv,*next;DINKLIST;下面给出双向链

29、表中插入删除一个结点的函数。操作过程见下图:例9-14 将一个结点插入到双向链表指定结点之后。insertafter(DINKLIST*current,DINKLIST*new)new-next=current-next;new-privcurrent;current-next-privnew;current-next-new;例9-15 将一个结点插入到双向链表指定结点之前。insertbefor(DINKLIST*current,DINKLIST*new)new-nextcurrent;new-priv=current-priv;current-priv=current-priv;curr

30、ent-priv=new;例9-16 在双向链表中删除一个指定结点。deleteelement(DINKLIST*current)current-next-priv=current-priv;current-priv-next=current-next;delete(current);9.3.59.3.5循环链表循环链表 单向链表的最后一个结点的指针域为空(NULL)。如果将这个指针里利用起来,以指向单向链表的第一个结点,就组成一个单向循环链表。如图9-15所示:9.3.69.3.6链表应用实例链表应用实例 例9.17 创建包含学号、姓名节点的单链表。其节点数任意个,表以学号为序,低学号的在前

31、,高学号的在后,以输入姓名为空作结束。在此链表中,要求删除一个给定姓名的节点,并插入一个给定学号和姓名的节点。程序如L9-17.c 例9-18 已有a,b两个链表,每个链表中的结点包括学号,成绩,要求把两个链表合并,按学号升序排列。程序如L9-18.c 本章小结本章小结1编译预处理Ansi C 标准规定可以在C源程序中加入一些“预处理命令”,以改进程序环境,提高编程效率。对于预处理命令,必须在程序编译之前,先对这些特殊命令进行“预处理”。经过预处理后程序不再包含预处理命令了。C语言提供的预处理功能主要有以下三种:A、宏定义B、文件包含C、条件编译分别用宏定义命令,文件包含命令,条件编译命令来实

32、现。为了与一般C语句相区别,这些命令以符号“#”开头。2 位运算 C语言提供了位运算的功能,这使得C语言也能像汇编语言一样用来编写系统程序。位运算符C语言提供了六种位运算符:&按位与|按位或 按位异或 取反 右移1)按位与运算 按位与运算符&是双目运算符。其功能是参与运算的两数各对应的二进位相与。只有对应的两个二进位均为1时,结果位才为1,否则为0。参与运算的数以补码方式出现。2)按位或运算 按位或运算符“|”是双目运算符。其功能是参与运算的两数各对应的二进位相或。只要对应的二个二进位有一个为1时,结果位就为1。参与运算的两个数均以补码出现。3)按位异或运算 按位异或运算符“”是双目运算符。其

33、功能是参与运算的两数各对应的二进位相异或,当两对应的二进位相异时,结果为1。参与运算数仍以补码出现。2位段 有些信息在存储时,并不需要占用一个完整的字节,而只需占几个或一个二进制位。例如在存放一个开关量时,只有0和1 两种状态,用一位二进位即可。为了节省存储空间,并使处理简便,C语言又提供了一种数据结构,称为“位域”或“位段”。所谓“位域”是把一个字节中的二进位划分为几个不同的区域,并说明每个区域的位数。每个域有一个域名,允许在程序中按域名进行操作。这样就可以把几个不同的对象用一个字节的二进制位域来表示。位段的定义和位域变量的说明位域定义与结构定义相仿,其形式为:struct 位段结构名 位段

34、列表;其中位域列表的形式为:类型说明符 位段名:位段长度。位段变量的说明与结构变量说明的方式相同。3链表 链表是一种重要的数据结构,原因就在于它可以动态的进行存储分配。链表都有一个头指针,用来存放整个链表的首地址。链表的定义形式如下:struct nodeint num;struct node*next;next用来存放下一节点的地址。如何进行动态的开辟和释放存储单元呢?c提供了以下有关函数:1)malloc(size)在内存的动态存储区开辟一个长度为size的连续空间。成功返回空间首地址,失败返回0;2)calloc(n,size)在内存的动态存储区开辟n个长度为size的连续空间。成功返回空间首地址,失败返回0;3)free(ptr)释放由ptr指向的内存区。Ptr是最近调用一次调用malloc和calloc时返回的值。上面函数中,n和size为整型,ptr为字符指针。请将实验报告请将实验报告(10次次)和作业本和作业本在下周二前交系办在下周二前交系办.否则后果否则后果自负自负.

展开阅读全文
相关资源
相关搜索

当前位置:首页 > 教育专区 > 大学资料

本站为文档C TO C交易模式,本站只提供存储空间、用户上传的文档直接被用户下载,本站只是中间服务平台,本站所有文档下载所得的收益归上传人(含作者)所有。本站仅对用户上传内容的表现方式做保护处理,对上载内容本身不做任何修改或编辑。若文档所含内容侵犯了您的版权或隐私,请立即通知得利文库网,我们立即给予删除!客服QQ:136780468 微信:18945177775 电话:18904686070

工信部备案号:黑ICP备15003705号-8 |  经营许可证:黑B2-20190332号 |   黑公网安备:91230400333293403D

© 2020-2023 www.deliwenku.com 得利文库. All Rights Reserved 黑龙江转换宝科技有限公司 

黑龙江省互联网违法和不良信息举报
举报电话:0468-3380021 邮箱:hgswwxb@163.com