版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领
文档简介
1、Good is good, but better carries it.精益求精,善益求善。C语言高级编程1C语言高级编程技术8.1递归程序设计8.1.1递归与递归程序设计递归技术在算法和程序设计中是一种十分有用的技术,C语言提供了支持递归定义的机制和手段。递归有直接递归和间接递归两种。在一个函数的定义中出现了对自身的调用,称之为直接递归;一个函数f的定义中包含了对函数g的调用,而g的实现过程又调用了f,即函数调用形成了一个环状调用链,这种方式称之为间接递归。PAGE#页:#例8.1编写一个递归函数,求n的阶乘值n!。若用fact(n)表示n的阶乘值,根据阶乘的数学定义可知:显然,当n0时,f
2、act(n)是建立在fact(n-1)的基础上。由于求解fact(n-1)的过程与求解fact(n)的过程完全相同,只是具体实参不同,因而在进行程序设计时,不必再仔细考虑fact(n-1)的具体实现,只需借助递归机制进行自身调用即可。于是求n的阶乘值fact(n)的具体实现为:longfact(intn)longm;if(n=0)return(1);elsem=n*fact(n-1);return(m);例8.2编写一个递归函数,求Fibonacci数列第n项的值。若用Fibona(n)表示Fibonacci数列第n项的值,根据Fibonacci数列的计算公式:可知当n2时,Fibonacci
3、数列第n项的值等于第n-1项的值与第n-2项的值相加之和,而Fibonacci数列第n-1项和第n-2项值的求解又分别取决于它们各自前两项之和。总之,Fibona(n-1)和Fibona(n-2)的求解过程与Fibona(n)的求解过程相同,只是具体实参不同。利用以上这种性质,我们在进行程序设计时便可以使用递归技术,Fibona(n-1)和Fibona(n-2)的求解只需调用函数Fibona自身加以实现即可。具体实现为:intFibona(intn)intm;if(n=1|n=2)return(1);elsem=Fibona(n-1)+Fibona(n-2);return(m);从上面两个实例
4、可以看出,要使用递归技术进行程序设计,首先必须将要求解的问题分解成若干子问题,这些子问题的结构与原问题的结构相同,但规模较原问题小。由于子问题与原问题结构相同,因而它们的求解过程相同,在进行程序设计时,不必再仔细考虑子问题的求解,只需借助递归机制进行函数自身调用加以实现,然后利用所得到的子问题的解组合成原问题的解即可;而递归程序在执行过程中,通过不断修改参数进行自身调用,将子问题分解成更小的子问题进行求解,直到最终分解成的子问题可以直接求解为止。综上所述,递归程序设计具有以下两个特点:(1)具备递归出口。递归出口定义了递归的终止条件,当程序的执行使它得到满足时,递归执行过程便终止。有些问题的递
5、归程序可能存在几个递归出口;(2)在不满足递归出口的情况下,根据所求解问题的性质,将原问题分解成若干子问题,子问题的求解通过以一定的方式修改参数进行函数自身调用加以实现,然后将子问题的解组合成原问题的解。递归调用时,参数的修改最终必须保证递归出口得以满足。8.1.2递归程序执行过程的分析递归程序的执行过程分为递推和回归两个阶段。在递推阶段,把较复杂的问题(规模为n)的求解推到比原问题简单一些的问题(规模小于n)的求解。例如例8.2中,求解Fibona(n),把它推到求解Fibona(n-1)和Fibona(n-2)。即是说,为计算Fibona(n),必须先计算Fibona(n-1)和Fibon
6、a(n-2),而计算Fibona(n-1)和Fibona(n-2),又必须先计算Fibona(n-3)和Fibona(n-4)。依次类推,直至计算Fibona(1)和Fibona(2),分别能立即得到结果1和1。在递推阶段,必须要有终止递归的情况。例如在函数Fibona中,当n为1和2的情况。在回归阶段,当获得最简单情况的解后,逐级返回,依次得到稍复杂问题的解,例如得到Fibona(1)和Fibona(2)后,返回得到Fibona(3)的结果,在得到了Fibona(n-1)和Fibona(n-2)的结果后,返回得到Fibona(n)的结果。在编写递归函数时要注意,函数中的局部变量和参数知识局限
7、于当前调用层,当递推进入“简单问题”层时,原来层次上的参数和局部变量便被隐蔽起来。在一系列“简单问题”层,它们各有自己的参数和局部变量。由于递归引起一系列的函数调用,并且可能会有一系列的重复计算,递归算法的执行效率相对较低。当某个递归算法能较方便地转换成递推算法时,通常按递推算法编写程序。例如上例计算斐波那契数列的第n项的函数Fibona(n)应采用递推算法,即从斐波那契数列的前两项出发,逐次由前两项计算出下一项,直至计算出要求的第n项。由于递归调用是对函数自身的调用,在一次函数调用未结束之前又开始了另一次函数调用。这时为函数的运行所分配的空间在结束之前是不能回收的,必须保留。这也意味着函数自
8、身的每次不同调用,就需要分配不同的空间。只有当最后一次调用结束后,才释放最后一次调用所分配的空间,然后返回上一层调用,调用结束后,释放调用所分配的空间,再返回它的上一层调用,这样逐层返回,直至返回到第一次调用,当第一次调用结束后,释放调用所分配的空间,整个递归调用才完成。在例8.1中,给出了一个求阶乘的函数。下面以求4!为例,其调用过程如图8-1所示。要求4!,即要求的fact(4)值。图8-1递归函数调用的执行过程8.1.3递归算法的优缺点递归函数的主要优点是可以把算法写的比使用非递归函数时更清晰更简洁,而且某些问题,特别是与人工智能有关的问题,更适宜用递归方法。递归算法的缺点,一是需要额外
9、的内存开销,特别是当递归层次较大时,递归函数需要占用的堆栈空间相当大。二是递归引起一系列的函数调用,并且可能会有一系列的重复计算,递归算法的执行效率相对较低。总之,递归算法要比解决同样问题的非递归算法效率低一些。内存空间需求更多一些。大多数用递归算法解决的问题,都可以找到相应的非递归算法,只有少数问题的求解只有递归算法。由于递归算法具有效率低、内存消耗大等缺点,在设计程序时,若有比较好的非递归算法,应尽量采用非递归算法。8.1.4递归程序设计的应用实例例8.3编程实现将正整数转换为字符串。要求在主函数中输入正整数,转换以及输出编一递归函数完成。本例的关键在于设计一个递归函数完成正整数n到字符串
10、的转换,实现该函数的一个基本思想为:从高位到低位分别取出n的每一位上的数字,将它们转换成对应的字符后,按其原有的顺序输出;而在此转换过程中,将n前面的若干位(除个位外)对应的整数转换成字符串的过程与将整个整数转换成字符串的过程完全相同,只是处理的对象不同,因此可以通过递归调用实现,然后在此基础上再将n的个位数字转换成字符输出即可。显然,若n前面的若干位(除个位外)对应的整数为0时,递归调用应该终止。程序为:#includevoidconvert(intn)inti,c;if(i=n/10)!=0)convert(i);c=n%10+0;putchar(c);voidmain()inta;sca
11、nf(%d,&a);convert(a);例8.4编程求两个正整数的最大公约数。要求编写一个递归函数求最大公约数。求最大公约数gcd(m,n)的求解公式为:由于以上最大公约数的定义本身即为递归定义,因此采用递归方式实现求m和n的最大公约数问题十分方便,将n=0作为递归的终止条件,其它情况只需按公式进行递归调用即可。程序为:#includeintgcd(intm,intn)intk;if(n=0)return(m);elseif(nm)return(gcd(n,m);elsek=m%n;return(gcd(n,k);voidmain()inta,b;scanf(%d%d,&a,&b);prin
12、tf(%d,gcd(a,b);例8.5汉诺塔(Hanoi)问题。汉诺塔问题是一个著名的问题。约十九世纪末,在欧洲的商店中出售一种智力玩具,在一块铜板上有三根杆,最左边的杆上自上而下、由小到大顺序串着由64个圆盘构成的塔,游戏的目的是将最左边A杆上的圆盘,借助最右边的C杆,全部移到中间的B杆上,条件是一次仅能移动一个盘,且不允许大盘放在小盘的上面。如图8-2所示。图8-2汉诺塔由于问题中给出的圆盘移动条件是:一次仅能移动一个盘,且不允许大盘放在小盘的上面,这样64个盘子的移动次数是:18,446,744,073,709,551,616。这是一个天文数字,若每一微秒可能计算(并不输出)一次移动,那
13、么也需要几乎一百万年。我们仅能找出问题的解决方法并解决较小n值时的汉诺塔,但目前由于计算机的速度还不够快,尚不可能用计算机解决64层的汉诺塔。按照上面给出的方法分析问题,找出移动圆盘的递归算法。设要解决的汉诺塔共有n个圆盘,对A杆上的全部n个圆盘从小到大顺序编号,最小的圆盘为1号,次之为2号,依次类推,则最下面最大的圆盘的编号为n。第1步,先将问题简化。假设A杆上只有一个圆盘,即汉诺塔只有一层n=1,则只要将1号盘从A杆上移到B杆上即可。第2步,对于一个有n(n1)个圆盘的汉诺塔,将n个圆盘分为两部分:上面的n-1个圆盘和最下面的n号圆盘。第3步,将上面的n-1个圆盘看成一个整体,为了解决n个
14、圆盘的汉诺塔,可以按如下方式进行操作:A杆上面的n-1个盘子,借助B杆,移到C杆上(如图8-3所示);图8-3A杆上剩下的n号盘子移到B杆上(如图8-4所示);图8-4整理上述分析结果,把第1步中化简问题的条件作为递归结束条件,将第3步分析得到的算法作为递归算法,可以写出如下完整的递归算法描述。定义一个函数movedisc(n,Aneedle,Bneedle,Cneedle)。该函数的功能是:将Aneedle杆上的n个圆盘,借助Cneedle杆,移动到Bneedle杆上。这样移动n个圆盘的递归算法描述如下:movedisc(n,Aneedle,Bneedle,Cneedle)if(n=1)将n
15、号圆盘从Aneedle上移到Bneedle;elsemovedisc(n-1,Aneedle,Cneedle,Bneedle)将n号圆盘从Aneedle上移到Bneedle;movedisc(n-1,Cneedle,Bneedle,Aneedle)按照上述算法可以编出如下程序。#includeinti=0;/*移动圆盘数量计数器*/voidmovedisc(unsignedintn,charAneedle,charBneedle,charCneedle)if(n=1)printf(%2d-(%2d):%c=%cn,+i,n,Aneedle,Bneedle);/*将Aneedle上的一个圆盘移到
16、Bneedle上*/elsemovedisc(n-1,Aneedle,Cneedle,Bneedle);/*将Aneedle上的n-1个圆盘借助Bneedle移到Cneedle上*/printf(%2d-(%2d):%c=%cn,+i,n,Aneedle,Bneedle);/*将Aneedle上的一个圆盘移到Bneedle上*/movedisc(n-1,Cneedle,Bneedle,Aneedle);/*将Cneedle上的n-1个圆盘借助Aneedle移到Bneedle上*/voidmain()unsignedn;printf(Pleaseenterthenumberofdiscs:);s
17、canf(%d,&n);/*输入n值*/movedisc(n,a,b,c);/*将A上的n个圆盘借助C将移动到B上*/printf(tTotal:%dn,i);8.2文本的屏幕输出和键盘输入8.2.1文本的屏幕输出显示器的屏幕显示模式有两种:文本方式和图形方式。文本方式就是只能显示字符的方式,在文本模式下屏幕上可以显示的最小单位是字符。在文本模式下,坐标原点在屏幕左上角,其坐标为(1,1),X轴为水平方向,Y轴为垂直方向。TurboC的字符屏幕函数主要包括文本窗口大小的设定、窗口颜色的设置、窗口文本的清除和输入输出等函数。这些函数的有关信息均包含在conio.h头文件中,因此在用户程序中使用这
18、些函数时,必须用include预处理命令将conio.h包含进程序。1)文本窗口的定义TurboC默认定义的文本窗口为整个屏幕,共有80列25行的文本单元。除了这种默认的80列25行的文本显示方式外,还可由用户通过textmode()函数来显式地设置TurboC支持的文本显示方式。Textmode()函数的函数原型为:voidtextmode(intnewmode);该函数将清除屏幕,以整个屏幕为当前窗口,并移光标到屏幕左上角。newmode参数的取值见表8-1,既可以用表中指出的方式代码,又可以用符号常量。LASTMODE方式指上一次设置的文本显示方式,它常用于在图形方式到文本方式的切换。方
19、式符号常量显示列行数和颜色0BW404025黑白显示1C404025彩色显示2BW808025黑白显示3C808025彩色显示7MONO8025单色显示-1LASTMODE上一次的显示方式表8-1TurboC支持的6种显示方式TurboC也允许用户根据自己的需要通过使用窗口设置函数window()重新设定显示窗口。window()函数的函数原型为:voidwindow(intleft,inttop,intright,intbottom);函数中形参(intleft,inttop)是窗口左上角的坐标,(intright,intbottom)是窗口的右下角坐标,其中(left,top)和(righ
20、t,bottom)是相对于整个屏幕而言的。例如,要定义一个窗口左上角在屏幕(20,5)处,大小为30列15行的窗口可写成:window(20,5,50,25);若window()函数中的坐标超过了屏幕坐标的界限,则窗口的定义就失去了意义,也就是说定义将不起作用,但程序编译连接时并不出错。窗口定义之后,用有关窗口的输入输出函数就可以只在此窗口内进行操作而不超出窗口的边界。一个屏幕可以定义多个窗口,但现行窗口只能有一个(因为DOS为单任务操作系统)。当需要用另一窗口时,可将定义该窗口的window()函数再调用一次,此时该窗口便成为现行窗口了。2)文本窗口颜色和其它属性的设置文本窗口颜色的设置包括
21、背景颜色的设置和字符颜色(即前景色)的设置,使用的函数及其原型为:设置背景颜色函数:voidtextbackground(intcolor);设置字符颜色函数:voidtextcolor(intcolor);有关颜色的定义见表8-2。表中的符号常数与相应的数值等价,二者可以互换。例如设定蓝色背景可以使用textbackground(1),也可以使用textbackground(BLUE),两者没有任何区别。符号常数数值含义背景或背景BLACK0黑前景、背景色BLUE1蓝前景、背景色GREEN2绿前景、背景色CYAN3青前景、背景色RED4红前景、背景色MAGENTA5洋红前景、背景色BROWN
22、6棕前景、背景色LIGHTGRAY7淡灰前景、背景色DARKGRAY8深灰用于前景色LIGHTBLUE9淡蓝用于前景色LIGHTGREEN10淡绿用于前景色LIGHTCYAN11淡青用于前景色LIGHTRED12淡红用于前景色LIGHTMAGENTA13淡洋红用于前景色YELLOW14黄用于前景色WHITE15白用于前景色BLINK128闪烁用于前景色表8-2颜色表TurboC另外还提供了一个函数,可以同时设置文本的字符和背景颜色,这个函数是文本属性设置函数,其函数原型为:voidtextattr(intattr);参数attr的值表示颜色形式编码的信息,每一位代表的含义如下:位7654321
23、0Bbbbcccc闪烁背景颜色字符颜色字节低四位cccc设置字符颜色,46三位bbb设置背景颜色,第7位B设置字符是否闪烁。假如要设置一个蓝底黄字,定义方法如下:textattr(YELLOW+(BLUE4);若再要求字符闪烁,定义变为:textattr(128+YELLOW+(BLUE4);注意:(1)对于背景只有0到7共八种颜色,取大于7小于15的数,则代表的颜色与减8后的值对应的颜色相同;(2)用textbackground()和textcolor()函数设置了窗口的背景与字符颜色后,在没有用clrscr()函数清除窗口之前,颜色不会改变,直到使用了函数clrscr(),整个窗口和随后输
24、出到窗口中的文本字符才会变成新颜色。(3)用textattr()函数时背景颜色应左移4位,才能使3位背景颜色移到正确位置。3)窗口内文本的输入输出函数(1)窗口内文本的输出函数前面介绍过的printf(),putc(),puts(),putchar()等输出函数以整个屏幕为窗口的,它们不受由window设置的窗口限制,也无法用函数控制它们输出的位置,但TurboC提供了三个文本输出函数,它们受窗口的控制,窗口内显示光标的位置,就是它开始输出的位置。当输出行右边超过窗口右边界时,自动移到窗口内的下一行开始输出,当输出到窗口底部边界时,窗口内的内容将自动产生上卷,直到完全输出完为止,这三个函数均受
25、当前光标的控制,每输出一个字符光标后移一个字符位置。这三个输出函数原型为:intcprintf(char*format,表达式表);intcputs(char*str);intputch(intch);它们的使用格式同printf(),puts()和putc(),其中cprintf()是将按格式化串定义的字符串或数据输出到定义的窗口中,其输出格式串同printf函数,不过它的输出受当前光标控制,且输出特点如上所述,cputs同puts,是在定义的窗口中输出一个字符串,而putch()则是输出一个字符到窗口,它实际上是函数putc的一个宏定义,即将输出定向到屏幕。(2)窗口内文本的输入函数可直接
26、使用stdio.h中的getch()或getche()函数。需要说明的是,getche()函数从键盘上获得一个字,在屏幕上显示的时候,如果字符超过了窗口右边界,则会被自动转移到下一行的开始位置。4)有关屏幕操作的函数voidclrscr(void);该函数将清除窗口中的文本,并将光标移到当前窗口的左上角处。voidclreol(void);该函数将清除当前窗口中从光标位置开始到本行结尾的所有字符,但不改变光标原来的位置。voiddelline(void);该函数将删除光标所在行的所有字符。voidgotoxy(intx,inty);该函数用于定位光标在当前窗口中的位置。这里x,y是指光标要定位
27、处的坐标(相对于窗口而言)。当x,y超出了窗口的大小时,该函数不起作用。intmovetext(intx1,inty1,intx2,inty2,intx3,inty3);该函数将把屏幕上左上角为(xl,y1),右下角为(x2,y2)的矩形内文本拷贝到左上角为(x3,y3)的一个新矩形区内。这里x,y坐标是以整个屏幕为窗口坐标系,即屏幕左上角为(1,1)。该函数与开设的窗口无关,且原矩形区文本不变。intgettext(intxl,intyl,intx2,inty2,void*buffer);该函数将把左上角为(xl,y1),右下角为(x2,y2)的屏幕矩形区内的文本存到由指针buffer指向的
28、一个内存缓冲区内,当操作成功,返回1;否则,返回0。因一个在屏幕上显示的字符需占显示存储器VRAM的两个字节,即第一个字节是该字符的ASCII码,第二个字节为属性字节,即表示其显示的前景、背景色及是否闪烁,所以buffer指向的内存缓冲区的字节总数的计算为:字节总数=矩形内行数每行列数2其中:矩形内行数=y2-y1+l,每行列数=x2-xl+1(每行列数是指矩形内每行的列数)。矩形内文本字符在缓冲区内存放的次序是从左到右,从上到下,每个字符占连续两个字节并依次存放。intputtext(intx1,inty1,intx2,inty2,void*buffer);该函数则是将gettext()函数
29、存入内存buffer中的文字内容拷贝到屏幕上指定的位置。注意:(1)gettext()函数和puttext()函数中的坐标是对整个屏幕而言的,即是屏幕的绝对坐标,而不是相对窗口的坐标;(2)movetext()函数是拷贝而不是移动窗口区域内容,即使用该函数后,原位置区域的文本内容仍然存在。例8.6:下面的程序首先定义了一个字符数组,下标为64,表示用来存四行八列的文本。由于没有用window函数设置窗口,因而用缺省值,即全屏幕为一个窗口,程序开始设置80列25行文本显示方式(C80),背景色为蓝色,前景色为红色,经clrscr函数清屏后,设置的背景色才使屏幕背景变蓝。gotoxy(10,10)
30、使光标移到第10行10列,然后在(10,10)开始位置显示L:load,接着在下面三行相同的列位置显示另外三条信息,13行10列显示的E:exit后面带有回车换行符,为的是将光标移到下一行开始处,好显示pressanykeytocontinue。当按任一键后,gettext函数将(10,l0,18,13)矩形区的内容存到ch缓存区内。ch即上述的四行八列信息,接着设置一个窗口,并纵向写上1,2,3,4,然后用movetext(),将此窗口内容复制到另一区域,由于此区域包括背景色和显示的字符,所以被复制到另一区域的内容也是相同的背景色和文本。当按任一键后,又出现提示信息,再按键,则存在ch缓冲区
31、内的文本由puttext()又复制到开设的窗口内了,注意上述的函数movetext(),gettext(),puttext()均与开设的窗口内坐标无关,而是以整个屏幕为参考系的。#include#includevoidmain()inti;charch4*8*2;/*定义ch字符串数组作为缓存区*/textmode(C80);textbackground(BLUE);textcolor(RED);clrscr();gotoxy(10,10);cprintf(L:load);gotoxy(10,11);cprintf(S:save);gotoxy(10,12);cprintf(D:delete)
32、;gotoxy(10,13);cprintf(E:exitrn);cprintf(Pressanykeytocontinue);getch();gettext(10,10,18,13,ch);/*存矩形区文存到ch缓存区*/clrscr();textbackground(1);textcolor(3);window(20,9,34,14);/*开一个窗口*/clrscr();cprintf(1.rn2.rn3.rn4.rn);/*纵向写1,2,3,4*/movetext(20,9,34,14,40,10);/*将矩形区文本复制到另一区域*/puts(hitanykey);getch();clr
33、scr();cprintf(pressanykeytoputtext);getch();clrscr();puttext(23,10,31,13,ch);/*将ch缓存区所存文本在屏上显示*/getch();5)状态查询函数有时需要知道当前屏幕的显示方式,当前窗口的坐标、当前光标的位置,文本的显示属性等,TurboC提供了一些函数得到屏幕文本显示有关信息的函数:voidgettextinfo(structtext_info*f);这里的text_info是在conio.h头文件中定义的一个结构,该结构的定义是structtext_infounsignedcharwinleft;/*窗口左上角x
34、坐标*/unsignedcharwintop;/*窗口左上角y坐标*/unsignedcharwinright;/*窗口右下角x坐标*/unsignedcharwinbottom;/*窗口右下角y坐标*/unsignedcharattributes;/*文本属性*/unsignedcharnormattr;/*通常属性*/unsignedcharcurrmode;/*当前文本方式*/unsignedcharscreenheight;/*屏高*/unsignedcharscreenwidth;/*屏宽*/unsignedcharcurx;/*当前光标的x值*/unsignedcharcury;/
35、*当前光标的y值*/;例8.7下面的程序将屏幕设置成80列彩色文本方式,并开了一个window(1,5,70,20)的窗口,在窗口中显示了currentinformationofwindow,然后用gettextinfo函数得到当前窗口的信息,后面的cprintf()函数将分别显示出结构text_info各分量的数值来。#include#includevoidmain()structtext_infocurrent;textmode(C80);textbackground(1);textcolor(13);window(1,5,70,20);clrscr();cputs(Currentinfo
36、rmationofwindowrn);gettextinfo(¤t);cprintf(Leftcornerofwindowis%d,%d,current.winleft,current.wintop);cprintf(Rightcornerofwindowis%d,%d,current.winright,current.winbottom);cprintf(Textwindowattributeis%d,current.attribute);cprintf(Textwindownormalattributeis%d,current.normattr);cprintf(Current
37、videomodeis%d,current.currmode);cprintf(Windowheightandwidthis%d,%d,current.screenheight,current.screenwidth);cprintf(Rowcursorposis%d,Columnposis%d,current.cury,current.curx);getch();8.2.2键盘输入当我们在键盘上按下某键时,系统是怎样知道是哪一个键被按下呢?它的奥妙在于计算机键盘内有一个微处理器,它用来扫描和检测每个键的按下和拾起状态。然后以程序中断的方式(INT9)与主机通信。ROM中BIOS内的键盘中断处
38、理程序,会将一个字节的按键扫描码(扫描码的06位标识了每个键在键盘上的位置,最高位标识按键的状态,0对应该键是被按下;1对应松开。它并不能区别大小写字母,而且一些特殊键如PrintScreen等不产生扫描码直接引起中断调用)翻译成对应的ASCII码。由于ASCII码仅有256个,它不能将PC键盘上的键全部包括,因此有些控制键如CTRL,ALT,END,HOME,DEL等用扩充的ASCII码表示,扩充码用两个字节的数表示。第一个字节是0,第二个字节是0255的数,键盘中断处理程序将把转换后的扩充码存放在Ax寄存器中,存放格式如表8-3所示。对字符键,其扩充码就是其ASCII码。键名AHAL字符键
39、扩充码=ASCII码ASCII码功能键/组合键扩充码0表8-3键盘扫描码是否有键按下,何键按下,简单的应用中可采用两种办法:一是直接使用TurboC提供的键盘操作函数bioskey()来识别,二是通过int86()函数,调用BIOS的INT16H,功能号为0的中断。它将按键的扫描码存放在Ax寄存器的高字节中。函数bioskey()的原型为:intbioskey(intcmd);它在bios.h头文件中进行了说明,参数cmd用来确定bioskey()如何操作,其含义见表8-4。cmd操作0bioskey()返回按健的键值,该值是2个字节的整型数。若没有键按下,则该函数一直等待,直到有键按下。当按
40、下时,若返回值的低8位为非零,则表示为普通键,其值代表该键的ASCII码。若返回值的低8位为0,则高8位表示为扩展的ASCII码,表示按下的是特殊功能键。1bioskey()查询是否有键按下。若返回非0值,则表示有键按下,若为0表示没键按下。2bioskey()将返回一些控制键是否被按过,按过的状态由该函数返回的低8位的各位值来表示:字节位对应的16进制数含义00 x01右边的shift键被按下10 x02左边的shift键被按下20 x04Ctrl键被按下30 x08Alt键被按下40 x10ScrollLock已打开50 x20NumLock已打开60 x40CapsLock已打开70 x
41、80Insert已打开表8-4函数bioskey(intcmd)中cmd的取值及含义当某位为l时,表示相应的键已按,或相应的控制功能已有效,如选参数cmd为2,若key值为0 x09,则表示右边的shift键被按,同时又按了Alt键。函数int86()的原型为:intint86(intintr_num,unionREGS*inregs,unionREGS*outregs);这个函数在bios.h头文件中进行了说明,它的第一个参数intr_num表示BIOS调用类型号,相当于intn调用的中断类型号n,第二个参数表示是指向联合类型REGS的指针,它用于接收调用的功能号及其它一些指定的入口参数,以
42、便传给相应的寄存器,第三个参数也是一个指向联合类型REGS的指针,它用于接收功能调用后的返回值,即出口参数,如调用的结果,状态信息,这些值从相关寄存器中得到。例8.8bioskey()函数的使用。#include#includevoidmain()intk1,k2,k;dok=bioskey(0);k1=k&0 x00FF;/*得到低8位的值*/k2=k8;/*得到高8位的值*/switch(k2)case71:printf(你按下了Home键!n);break;case79:printf(你按下了End键!n);break;case73:printf(你按下了PgUp键!n);break;c
43、ase81:printf(你按下了PgDn键!n);break;default:printf(你按下了Home,End,PgUp,PgDn之外的键!n);while(k1!=27);/*ESC键才退出*/8.3图形程序设计计算机图形程序设计是程序设计中比较难,但又吸引人的部分。因在ANSIC中没有对图形库的要求,所以不同版本的C语言编译程序提供的图形函数不一样。本节以TurboC的图形库来介绍图形程序设计。8.3.1图形模式的初始化要进行图形程序设计,就要将屏幕显示模式设置为图形模式。要设置图形模式,可以用TurboC提供的图形初始化函数:voidfarinitgraph(intfar*gdr
44、iver,intfar*gmode,charfar*pathtodriver);其中,gdriver表示图形驱动器,gmode表示图形模式,pathtodriver表示图形驱动程序所在的目录路径,若图形驱动程序在TurboC的默认目录下,可将参数pathtodriver设置为空字符串()。若不知道所用的图形显示器适配器的种类,在调用图形初始化函数时,设置参数gdriver的值为0或DETECT,由系统自动进行硬件检测。TurboC提供的图形驱动器、模式的符号常量及数值的意义参见TurboC提供的graphics.h文件。例如在程序的开始部分可进行如下的描述来自动检测图形显示器适配器。#incl
45、ude#includevoidmain()intgd=DETECT,gm;/*自动检测*/initgraph(&gd,&gm,);/*图形模式初始化*/TurboC提供的图形与字形驱动文件见表8-5文件名用途ATT.BGIAT&T图形驱动文件CGA.BGICGA图形驱动文件EGAVGA.BGIEGA和VGA图形驱动文件IBM8514.BGIIBM8514图形驱动文件GOTH.CHR歌特笔划字形文件LITT.CHR小号笔划字形文件SANS.CHR无衬线矢量笔划字形文件TRIP.CHR三重矢量笔划字形文件表8-5TurboC图形与字形驱动文件表8.3.2图形模式下的坐标系在图形模式下,屏幕上每个像
46、素的显示位置用点坐标来描述。图形模式下的坐标系如图8-5所示。坐标原点在屏幕左上角,其坐标为(0,0),X轴为水平方向,Y轴为垂直方向。图8-5图形模式下的坐标系8.3.3屏幕图形的色彩与相关操作在C语言中,图形模式的屏幕颜色设置分为背景色的设置和作图色的设置。背景色的设置通过对函数setbkcolor(intcolor),作图色的设置通过对函数setcolor(intcolor)的调用来实现。其中intcolor为整型数据,取值范围为015,数字的表示的颜色见表8-2。背景和前景色可以有16种颜色。(这种方式成为调色板方式)图形屏幕相关的操作的主要函数如表8-6所示:函数功能cleardev
47、ice()清除图形屏幕clearriwport()清除当前视区setbkcolor()设置图形背景颜色Setcolor()设置图形前颜色setfillstyle()设置填充模式和填充颜色settextstyle()设置文本字符的显示模式closegraph()关闭图形系统,返回文本方式表8-6屏幕图形的色彩与相关操作的函数几个函数的说明如下:(1)setfillstyle()为填充函数,其作用是对图形内部填充颜色,函数原型为:voidfarsetfillstyle(intpattern,intcolor);其中,pattern表示填充模式,color表示颜色参数。pattern的取值见表8-7
48、符号数值描述EMPTY_FILL0用背景色填充SOLID_FILL1单色填充LIN_FILL2用一填充LTSLASH_FILL3用/填充SLASH_FILL4用粗填充BKSLASH_FILL5用粗/填充LIBKSLASH_FILL6用填充HATCH_FILL7用淡影线填充XHATCH_FILL8用交叉线填充INTERLEAVE_FILL9用间隔线填充WIND_EDOT_FILL10用稀疏空白点填充CLOSEDOT_FILL11用密集空的点填充USER_FILL12用户定义的填充模式表8-7图形填充模式表(2)settextstyle()函数,用于设置文本字符串的字型、输出方向和字符大小。函数原
49、型为:voidfarsettextstyle(intfont,intdirection,intcharsize);其中,font用于设置字符的字体,其取值见表8-8direction用于设置字符输出方向,其取值见表8-9charsize用于设置字符大小,其取值见表8-10符号常量数值含义DEFAULT_FONT08*8点阵字(缺省值)TRIPLEX_FONT13倍笔划字体SMALL_FONT2小号笔划字体SANS_SERIF_FONT3无衬线笔划字体GOTHIC_FONT4黑体笔划字体表8-8font的取值符号常量数值含义HORIZ_DIR0从左到右VERT_DIR1从底到顶表8-9direc
50、tion的取值符号常量或数值含义符号常量或数值含义18*8点阵756*56点阵216*16点阵864*64点阵324*24点阵972*72点阵432*320点阵1080*80点阵540*40点阵USER_CHAR_SIZE=0用户定义的字符大小648*48点阵表8-10charsize的取值8.3.4基本绘图函数图形由点、线、面组成,TurboC提供了一些函数,以完成这些操作,而所谓面则可由对封闭图形填上颜色来实现。1)画点函数voidfarputpixel(intx,inty,intcolor);该函数表示在指定的x,y位置画一点,点的显示颜色由设置的color值决定,关于颜色的设置,将在设
51、置颜色函数中介绍。intfargetpixel(intx,inty);该函数与putpixel()相对应,它得到在(x,y)点位置上的象素的颜色值。例89:下面是一个画点的程序,它将在y=20的恒定位置上,沿x方向从x=200开始,连续画两个点(间距为4个象素位置),又间隔16个点位置,再画两个点,如此循环,直到x=300为止,每画出的两个点中的第一个由putpixel(x,20,1)所画,第二个则由putplxel(x+4,20,2)画出,颜色值分别设为1和2。#includevoidmain()intgraphdriver=DETECT,graphmode,x;initgraph(&gra
52、phdriver,&graphmode,);cleardevice();for(x=20;x=300;x+=16)putpixel(x,20,1);putpixel(x+4,20,2);getch();closegraph();2)有关画图坐标位置的函数在屏幕上画线时,如同在纸上画线一样。画笔要放在开始画图的位置,并经常要抬笔移动,以便到另一位置再画。我们也可想象在屏上画图时,有一无形的画笔,可以控制它的定位、移动(不画),也可知道它能移动的最大位置限制等。完成这些功能的函数是:voidfarmoveto(intx,inty);移动画笔到指定的(x,y)位置,移动过程不画。voidfarmov
53、erel(intdx,intdy);画笔从现行位置(x,y)处移到一位置增量处(x+dx,y+dx),移动过程不画。intfargetx(void);得到当前画笔的x位置。intfargety(void);得到当前画笔的y位置3)画线函数这类函数提供了从一个点到另一个点用设定的颜色画一条直线的功能,起始点的设定方法不同,因而有下面不同的画线函数:voidfarline(intx0,inty0,intx1,inty1);从(x0,y0)点到(x1,y1)点画一直线。voidfarlineto(intx,inty);从现行画笔位置到(x,y)点画一直线。voidfarlinerel(intdx,i
54、ntdy);从现行画笔位置(x,y)到位置增量处(x+dx,y+dy)画一直线。例8.10:下面的程序将用moveto函数将画笔移到(100,20)处,然后从(100,20)到(100,80)用1ineto函数画一直线。再将画笔移到(200,20)处,用lineto画一直线到(100,80)处,再用line函数在(100,90)到(200,90)间连一直线。接着又从上次1ineto画线结束位置开始(它是当前画笔的位置),即从(100,80)点开始到x增量为0,y增量为20的点(100,100)为止用linerel函数画一直线。moverel(-100,0)将使画笔从上次用1inerel(0,2
55、0)画直线时的结束位置(100,100)处开始移到(100-100,100-0),然后用linerel(30,20)从(0,100)处再画直线至(0+30,100+20)处。用line函数画直线时,将不考虑画笔位置,它也不影响画笔原来的位置,lineto和1inerel要求画笔位置,画线起点从此位置开始,而结束位置就是画笔画线完后停留的位置,故这两个函数将改变画笔的位置。#includevoidmain()intgraphdriver=DETECT,graphmode;initgraph(&graphdriver,&graphmode,);cleardevice();moveto(100,20
56、);lineto(100,80);moveto(200,20);lineto(100,80);line(100,90,200,90);linerel(0,20);moverel(-100,0);linerel(30,20);getch();closegraph();4)画矩形和条形图函数画矩形函数rectangle将画出一个矩形框,而画条形函数bar将以给定的填充模式和填充颜色画出一个条形图,而不是一个条形框,关于填充模式和颜色将在后面介绍。voidfarrectangle(intxl,inty1,intx2,inty2);该函数将以(x1,y1)为左上角,(x2,y2)为右下角画一矩形框。v
57、oidbar(intx1,inty1,intx2,inty2);该函数将以(xl,y1)为左上角,(x2,y2)为右下角画一实形条状图,没有边框,图的颜色和填充模式可以设定。若没有设定,则使用缺省模式。5)画椭圆、圆和扇形图函数在画图的函数中,有关于角的概念。在TurboC中是这样规定的:屏的x轴方向为0度,当半径从此处逆时针方向旋转时,则依次是90度、180度、270度,当360度时,则和x轴正向重合,即旋转了一周。如图8-6所示。图8-6起始角和终止角voidellipse(intx,inty,intstangle,intendangel,intxradius,intyradius);该函
58、数将以(x,y)为中心,以xradius和yradius为x轴和y轴半径,从起始角stangle开始到endangle角结束,画一椭圆线。当stangle=0,endangle=360时,则画出的是一个完整的椭圆,否则画出的将是椭圆弧。关于起始角和终止角规定如图8-2所示。voidfarcircle(intx,inty,intradius);该函数将以(x,y)为圆心,radius为半径画个圆。voidfararc(intx,inty,intstangle,intendangle,intradius);该函数将以(x,y)为圆心,radius为半径,从stangle为起始角开始,到endang
59、le为结束角画一圆弧。voidfarpieslice(intx,inty,intstangle,intendangle,intradius);该函数将以(x,y)为圆心,radius为半径,从stangle为起始角,endangle为结束角,画一扇形图,扇形图的填充模式和填充颜色可以事先设定,否则以缺省模式进行。例8.11:该程序将用e11ipse函数画椭圆,从中心为(320,100),起始角为0度,终止角为360度,x轴半径为75,y轴半径为50画一椭圆,接着用circle函数以(320,220)为圆心以半径为50画圆。然后分别用pieslice和e11ipse及arc函数在下方面出了一扇形
60、图和椭圆弧及圆弧。#includevoidmain()intgraphdriver=DETECT,graphmode;initgraph(&graphdriver,&graphmode,);cleardevice();ellipse(320,100,0,360,75,50);circle(320,220,50);pieslice(320,340,30,150,50);ellipse(320,400,0,180,100,35);arc(320,400,180,360,50);getch();closegraph();8.3.5图形设计举例例812:绘制钻石图案。将一圆周进行n等分,然后将所有的等
温馨提示
- 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
- 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
- 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
- 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
- 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
- 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
- 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。
最新文档
- 医疗服务质量信息公开管理制度
- 医院暖气管道拆除安全施工方案
- 公益活动印刷服务支持方案
- 2024-2025学年黑龙江省龙东联盟高三上学期10月月考生物试题及答案
- 流行病学调查中的院感管理策略
- 电力设施建设合同条款
- 医疗机构运营安全及质量提升方案
- 娱乐场所安全应急方案
- 少年宫儿童安全防性侵方案
- 平安社区建设评价指标体系构建研究
- 第二版《高中物理题型笔记》上册
- 上海市大学生安全教育(2022级)学习通课后章节答案期末考试题库2023年
- 苏轼生平及创作整理
- 柴油发电机组应急预案
- 语文《猜猜他是谁》教案
- 绘本:让谁先吃好呢
- 宽容待人正确交往中小学生教育主题班会
- 移动通信网络运行维护管理规程
- 龙头股战法优质获奖课件
- 小班幼儿语言活动教案100篇
- 中国青瓷艺术鉴赏智慧树知到答案章节测试2023年丽水学院
评论
0/150
提交评论