C语言高级程序设计.ppt

上传人:tia****nde 文档编号:12706235 上传时间:2020-05-14 格式:PPT 页数:83 大小:497KB
返回 下载 相关 举报
C语言高级程序设计.ppt_第1页
第1页 / 共83页
C语言高级程序设计.ppt_第2页
第2页 / 共83页
C语言高级程序设计.ppt_第3页
第3页 / 共83页
点击查看更多>>
资源描述
C语言程序设计,湖南工学院,第9章C语言高级程序设计,9.1编译预处理命令9.2位运算9.3结构体高级应用链表本章小结,9.1编译预处理命令,ANSIC标准规定可以在C源程序中加入一些“预处理命令”,以改进程序环境,提高编程效率。C程序中编译预处理语句的作用不是实现程序的功能,它们是发送给编译系统的信息。也就是说,对于预处理命令,必须在程序编译之前,先对这些特殊命令进行“预处理”。经过预处理后程序不再包含预处理命令了。C语言提供的预处理功能主要有宏定义、文件包含及条件编译三种。分别用宏定义命令,文件包含命令,条件编译命令来实现。为了与一般C语句相区别,这些命令以符号“#”开头。,9.1.1宏,宏定义功能是定义符号常量和常参数的宏,宏定义编译预处理语句的格式如下:#define字符串1字符串2它把字符串1定义为字符串2,字符串1称为字符串2的宏定义,例如,下面是符号常量的宏定义:#defineON1#defineOFF0它把符号常量ON定义为1,OFF定义为0。符号常量经过宏定义后,就可以在程序中作为常量使用。例如:if(a=ON)printf(“SwitchisONn”);elseif(a=OFF)printf(“SwitchisOFFn”);,在系统执行编译预处理过程时,将把程序中出现的字符串1一律用字符串2置换,就是说程序中的符号常量用定义它们的常量置换,然后再对置换处理后的源文件进行编译。如上面程序段经编译预处理后成为下列形式:if(a=1)printf(“SwitchisONn”);elseif(a=2)printf(“SwitchisOFFn”);在宏定义语句中,可以使用已经定义过的符号常量定义新的符号常理。例如:#defineWID40#defineLEN(WID+20)其中第二个宏定义中使用了第一个宏定义的符号常量WID。在执行编译预处理时,程序中出现的所有符号常量WID都将被40置换,所有的符号常量LEN,不带参数的宏定义:用一个指定的标识符(即名字)来代表一个字符串。一般形式:#define标识符字符串例:#definePI3.14159说明:(1)宏名一般习惯用大写字母表示。(2)使用宏名代替一个字符串,可以减少程序中重复书写某些字符串的工作量。(3)宏定义是用宏名代替一个字符串,也就是作简单的置换,不作正确性检查。(4)宏定义不是C语句,不必在行末加分号.#definePI3.14159;area=PI*r*r;展开:area=3.14159;*r*r;出现语法错误(5)#define命令出现在程序中函数的外面,宏名的有效范围为定义命令之后到本源文件结束。(6)可以用#undef命令终止宏定义的作用域。格式:#undef宏名,(7)在进行宏定义时,可以引用已定义的宏名,可以层层置换。#defineR3.0#definePI3.14159#defineL2*PI*R#defineSPI*R*Rmain()printf(“L=%fnS=%fn”,L,S);(8)对程序中用双括号括起来的字符串内的字符,即使与宏名相同,也不进行置换。(9)宏定义是专门用于预处理命令,只作字符替换。,带参数的宏定义:不是进行简单的字符串替换,还要进行对数替换。一般形式:#define宏名(参数表)字符串例:#defineS(a,b)a*barea=S(3,2)#definePI3.14159#defineS(r)PI*r*rmain()floata,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则#defineS(r)PI*(r)*(r),都将被(40+20)置换。例如,程序中的下列语句:area=LEN*WID;在执行编译预处理时,该语句将被置换成:area=(40+20)*40;经运算后变量area的值是2400。从上面的置换过程可以看到,LEN定义时包围WID+20的圆括号是不可缺少的,若上面的宏定义时不使用圆括号:#defineLENWID+20则上面的area赋值表达式在编译预处理后成为:area=40+20*40;这时变量area的计算结果值是840,它并不是预定的计算结果。因此,在进行宏定义时,为了保证宏定义被置换后仍保持正确的运算顺序,经常在定义式中使用必要的圆括号包围定义的式子。,在C语言程序中,宏定义语句除了定义符号常量外,还经常用于定义带参数的宏,带参数的宏是在定义的宏定义中可以带有若干参数。例如:#defineMULT2(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。当程序中需要计算某两个变量和的平方时,如果使用上面定义的带参数的宏的话,如下所示:w=6;v=4;c=MULT2(w+v);进行编译预处理后,上面的赋值表达式置换后的形式是:c=w+v*w+v;它的运算顺序与预定的顺序完全不同,计算结果是34。如果上面的宏定义改为下列形式:#defineMULT2(X)(X)*(X)上面的赋值表达式置换后就成为:c=(w+v)*(w+v);它的运算结果就正确了。这里又一次看到在定义式中使用必要圆括号的重要性。,【例9.1】程序中的宏定义(计算球的体积)程序如L9-1.c该程序在编译预处理中,计算球体积的表达式语句:v=4*PI*MULT3(r)/3;其中的两个宏定义PI和MULT3将分别由定义它们的常量和表达式进行置换,实际上参加编译的语句如下所示:v=4*3.1415926*(r)*(r)*(r)/3;在程序设计时,经常把那些反复使用的运算表达式定义不带参数的宏,这样一方面使程序更加简洁,另一方面可以使运算的意义更加明显。下面再给出几个带参数宏的例子,它们都是使用三项条件表达式定义的。#definemin(x,y)(x=0)?x:-x)求x的绝对值。#definesign(x)(x0)?1:(x”右移运算符“”的使用方式为:运算对象右移位数右移运算符将运算对象的每个二进制位同时向右移动指定的位数,从右边移出的低位部分被丢弃。对无符号数,左边空出的高位补0;对有符号数,正数的高位部分补0,负数的高位部分补0还是1和计算机系统有关。移入0的称为“逻辑右移”,移入1的称为“算术右移”。,“逻辑右移”相当于无符号数除以2,“算术右移”相当于有符号数除以2。例如:a:1001011111101101a1:0100101111110110-逻辑右移a1:1100101111110110-算术右移7位复合赋值运算符类似于算术运算的复合运算符,位运算符和赋值运算符也可以构成“复合赋值运算符”。如9-2所示。,8位运算的应用(1)键盘扫描码键盘上除了ASC|码外,还有非ASC|码(如左移键“”对应的编码),叫扩充键盘码。我们把扩充键盘码放在高八位,ASC|码放在低八位所组成的代码称为扫描码。对于某一特定的扫描码,若其低八位不为零,则此八位就是相应字符的ASC|码值;若低八位是零,则高八位是扩充键盘码,需要再读取入第二个字节,根据它的值来判断它是那一个功能键。表9-3给出了单功能键和组合功能键的键值,表中代码,系指第二个字节键值的十进制数。例如,“”的扫描码低八位应为零,而高八位是0X4B,所以,“”键的扫描码为0X4B00;回车键也有对应的ASC|码,故其扫描码的低八位是回车键的ASC|码值0X0D。,(2)测试键盘扫描码调用标准库函数bioskey()读取键盘扫描码,再通过位运算分离低八位字符的ASC|码和高八位的扩充键盘码。注意使用bioskey()函数,应在头部加上#include“bios.h”。例910利用bioskey()函数,测试键盘扫描码,按ESC键退出。算法设计:(1)利用位的“与”运算,提取低8位low=key,程序如L9-10.c说明:(1)关于函数bioskey()函数bioskey()的功能是用于识别用户按键和获得按键值。函数原型是:intbioskey(intcmd);在中定义,在使用它时,应用include命令将bios.h文件包含进来。其中参数cmd可取0或1。当cmd=1时,检测键盘是否击键,如果没有击键,函数将返回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成为右面的第47位,即:xnext;/*跟踪链表的增长,即指针后移*/if(strcmp(temp-str,pstr)=0)/*找到字符串*/if(temp=head)/*表头节点*/printf(deletestring:%sn,temp-str);head=head-next;free(temp);/*释放被删节点*/elsep-next=temp-next;/表*中节点*/printf(deletestring:%sn,temp-str);free(temp);,elseprintf(nnofindstring!n);没/找*到要删除的字符串*/return(head);/*返回表头指针*/2.链表的插入首先定义链表的结构:structintnum;/*学生学号*/charstr20;/*姓名*/structnode*next;在建立的单链表中,插入节点有三种情况,如图9-7所示。,插入的节点可以在表头、表中或表尾。假定我们按照以学号为顺序建立链表,则插入的节点依次与表中节点相比较,找到插入位置。由于插入的节点可能在链表的头,会对链表的头指针造成修改,所以定义插入节点的函数的返回值定义为返回结构体类型的指针。节点的插入函数如下:structnode*insert(head,pstr,n)/*插入学号为n、姓名为pstr的节点*/structnode*head;/*链表的头指针*/char*pstr;intn;structnode*p1,*p2,*p3;p1=(structnode*)malloc(sizeof(structnode)分;配/*一个新节点*/strcpy(p1-str,pstr);/*写入节点的姓名字串*/p1-num=n;/*学号*/,p2=head;if(head=NULL)/*空表*/head=p1;p1-next=NULL;/*新节点插入表头*/else/*非空表*/while(np2-num/*跟踪链表增长*/if(nnum)/*找到插入位置*/if(head=p2)/*插入位置在表头*/,head=p1;p1-next=p2;else/*插入位置在表中*/p3-next=p1;p1-next=p2;else/*插入位置在表尾*/p2-next=p1;p1-next=NULL;return(head);/*返回链表的头指针*/,9.3.3遍历链表,由于链表是一个动态的数据结构,链表的各个结点由指针链接在起,访问链表元素时通过每个链表结点的指针逐个找到该结点的下一个结点,直找到链表尾,链表的最后一个结点的指针为空。例如:编历链表函数。voidoutputlist(LINKLIST*head)LINKLIST*currenthead-next;while(current!NULL)printf(%dn,current-info);current=current-next;return;,9.3.4双向链表,每个结点中只包括一个指向下个结点的指针域,这种链表称为单向链表。如果要在单向链表一个指针所指的当前位置插入一个新结点,就必须从链表头指针开始逐个遍历直到当前指针所指结点的前一结点,修改这个结点的指针。双向链表的每个结点中包括两个指针域,分别指向该结点的前一个结点和后一个结点。在双向链表中由任何一个结点都很容易找到其前面的结点和后面的结点,而不需要在上述的插入(及删除)操作中由头结点开始寻找。定义双向链表的结点结构为typedefstructnodeDATATYPEinfo;node*priv,*next;DINKLIST;,下面给出双向链表中插入删除一个结点的函数。操作过程见下图:例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;current-priv=new;例9-16在双向链表中删除一个指定结点。deleteelement(DINKLIST*current)current-next-priv=current-priv;current-priv-next=current-next;delete(current);,9.3.5循环链表,单向链表的最后一个结点的指针域为空(NULL)。如果将这个指针里利用起来,以指向单向链表的第一个结点,就组成一个单向循环链表。如图9-15所示:,9.3.6链表应用实例,例9.17创建包含学号、姓名节点的单链表。其节点数任意个,表以学号为序,低学号的在前,高学号的在后,以输入姓名为空作结束。在此链表中,要求删除一个给定姓名的节点,并插入一个给定学号和姓名的节点。程序如L9-17.c例9-18已有a,b两个链表,每个链表中的结点包括学号,成绩,要求把两个链表合并,按学号升序排列。程序如L9-18.c,本章小结,1编译预处理AnsiC标准规定可以在C源程序中加入一些“预处理命令”,以改进程序环境,提高编程效率。对于预处理命令,必须在程序编译之前,先对这些特殊命令进行“预处理”。经过预处理后程序不再包含预处理命令了。C语言提供的预处理功能主要有以下三种:A、宏定义B、文件包含C、条件编译分别用宏定义命令,文件包含命令,条件编译命令来实现。为了与一般C语句相区别,这些命令以符号“#”开头。2位运算C语言提供了位运算的功能,这使得C语言也能像,汇编语言一样用来编写系统程序。位运算符C语言提供了六种位运算符:其中位域列表的形式为:类型说明符位段名:位段长度。位段变量的说明与结构变量说明的方式相同。,3链表链表是一种重要的数据结构,原因就在于它可以动态的进行存储分配。链表都有一个头指针,用来存放整个链表的首地址。链表的定义形式如下:structnodeintnum;structnode*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次)和作业本在下周二前交系办.否则后果自负.,
展开阅读全文
相关资源
正为您匹配相似的精品文档
相关搜索

最新文档


当前位置:首页 > 图纸专区 > 课件教案


copyright@ 2023-2025  zhuangpeitu.com 装配图网版权所有   联系电话:18123376007

备案号:ICP2024067431-1 川公网安备51140202000466号


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