资源描述:
《C语言全程学习课件.ppt》由会员上传分享,免费在线阅读,更多相关内容在教育资源-天天文库。
C语言程序设计基础 前言本电子讲稿是在近几年作者为计算机专业、计算机第二专业授课讲稿的基础上,为2000级计算机科学与技术(本科)(80学时)、计算机应用与维护及计算机网络专业(60学时)授课而开发的。在开发中借鉴了作者以前开发的《QUICKBASIC程序设计》及《计算机应用基础》电子讲稿的经验。在2000级授课完成后,对讲稿进行了修改。为使该讲稿适合99信息试点班(100学时)的教学,又根据教学要求,对讲稿的内容进行了一次较大规模的增加。此次用该讲稿给2001级计算机科学与技术班(80学时)授课时,又结合本人主持的教育部教学科研课题,按照现代新的认知教学理论—建构主义的教学理论,对讲稿进行了第三次大规模的修改。本次修改,主要特色如下: 1根据人们学习程序语言的规律,讲稿全部以实例为主线。以这种方式教学,首先介绍实际问题,接着介绍问题的解决办法,也就是算法和数据结构,然后用伪代码将问题描述出来,最后将伪代码翻译成C语言程序(当然也可以是其它语言的程序)。对于程序中碰到的新的语句或函数,确改变了以往的程序语言教学的一般做法:一开始,先介绍语句或函数,然后介绍用法(语法),再用一个小例子说明该语句或函数的使用,最后,罗列一大堆有关该语句或函数的若干细节。这样的教学方法,对学生参加某些考试是很有帮助的。但要让学生在学完语言课程后编制一个稍复杂的程序,往往就无从下手。这样学习,很多学生学了一些语法或规律,但弄不明白为什么要这样。以实例为主线,学生在学习程序之后,对不懂的语法现象再学习,就有了感性认识,也比较容易接受;2利用超级链接功能模拟人在学习时的联想,使得讲稿可以从任何一部分进入,无需从头开始学习; 4在以往的教材中,例题大多是为了说明语法而设计的,因此,各例题之间少有联系。在本电子讲稿中,大多数的例题之间是有联系的,单独使用时可以用来说明语言的语法现象,到课程结束时,这些例题又可以组合成一个较大的程序,以帮助学生进行程序设计训练。3在教学中,我们发现,学生往往能读懂一些简单的程序,但对于一些简单的问题,要自己进行程序设计比较困难。讲稿中的例题,除少量较容易的例题外,全部给予了详细的算法分析,改变了以往教材或教学中的给出问题,然后给出程序,再给学生讲解程序。本讲稿以C程序作为基础,但对于其它高级语言的学习,也有参考价值。 第1章C语言简介第2章顺序程序设计第3章分支结构第4章循环结构第8章结构与共用体第11章位运算第10章Cwindows程序设计简介第7章指针第5章函数第9章文件第6章数组 第一章C语言简介一.程序的概念程序就是一系列的操作步骤,计算机程序就是由人事先规定的计算机完成某项工作的操作步骤。每一步骤的具体内容由计算机能够理解的指令来描述,这些指令告诉计算机“做什么”和“怎样做”。二.程序设计语言的概念编写计算机程序所使用的语言称为程序设计语言。 四高级程序设计语言(简称高级语言)高级语言是相对低级语言而言的。低级语言是计算机能直接识别的语言(机器语言)或符号化的机器语言(汇编语言),高级语言是一种通用的,面向用户的各类需要,与特定的机器相分离,并遵循一定的严格规定与形式的语言,其语言格式接近于自然语言或接近于数学函数形式。三计算机语言发展过程机器语言汇编语言高级语言 BASIC,QUICKBASIC,FORTRAN,COBOL,PASCAL,C,LISP,PROLOG,ADA,PL/1,VISUALBASIC,C++五常用高级语言六过程式语言面向解题过程,告诉计算机“怎么做”,这种语言称为过程式语言。面向处理对象,告诉计算机“做什么”而不必指出“怎么做”,计算机就能完成所要求的任务.这种语言称为非过程式语言.BASIC,QUICKBASIC,FORTRAN,PASCAL,C等均为过程式语言,SQL查询语言,SMALLTALK等均为非过程式语言. 七解释型与编译型语言计算机不能直接识别高级语言,要让计算机能执行高级语言,需要将高级语言翻译成等价的机器语言,这种翻译有两种形式:(1)边翻译边执行;(2)全部翻译完成后才执行。前一种方式称为解释方式,后一种称为编译方式.C,FORTRAN,PASCAL等为编译型语言BASIC,java等为解释型语言QUICKBASIC有解释和编译两种方式.用高级语言写成的程序称为源程序(或源代码)翻译成的机器语言称为目标程序(目标代码) 八C语言的特点1语言简单、紧凑、使用方便、灵活。C语言一共只有32个关键字,9种控制语句,大小写区别;2运算符丰富共有34种运算符,表达式类型多样化,使用灵活;3数据结构丰富,具有现代化的语言的各种结构;4具有结构化的控制语句,用函数作为程序模块,以实现程序的模块化,是结构化的理想语言,符合现代编程风格的要求; 7生成的目标代码质量高,一般只比汇编语言生成的目标代码效率低10%~20%;8用C语言编写的程序可移植性好(与汇编语言相比)。6C语言允许直接访问物理地址,能进行位(bit)操作,可以直接对硬件进行操作,能实现汇编语言的大部分功能;5语法限制不太严格,程序设计自由度大。例如,对数组下标越界不作检查,对变量的类型使用比较灵活。如整型量与字符型数据及逻辑型数据可通用。 九C语言学习中要注意的问题及学习方法1C语言关键字少,而且与其它高级语言类似,掌握标准C并不难。2C语言是基于函数的语言,C语言的大部分功能需要调用库函数去实现。DOS下的C编译器TurboC2.0大约有440个函数,BC++2.0大约有500多个函数,VC++除了标准的C运行库之及C语言扩展外,还有600多个WindowsAPI,以及VC++中的类库,Linux的C也有很多扩展。要掌握好库函数是不太容易的。 3C语言可以直接对硬件进行操作,因此,对计算机的硬件要有一定的了解,否则,就发挥不了C语言的这一特长。4C语言的指针使用极为灵活,这往往会给使用者带来困难。5C语言的语法不太严格,给熟练的使用者以很大的灵活性,但给初学者带来很多困难,尤其是程序的调试。学会调试程序是非常重要的。6学习计算机语言要多实践,学习C语言更要多实践,只有通过大量的上机实践才能真正掌握C语言。 十其它常用高级语言面向对象的C语言。它是在C语言的基础上增加了面向对象的内容。C++的学习比C语言更为困难。从目前的发展看,C++的应用更为普及。1C++语言PASCAL语言是一种有着良好结构化特性的高级语言,它是在软件危机的70年代所创造的一种完全符合结构化原则,有着严格的语法规则的高级语言。该语言在语言教学中有着良好的声誉,但在实际使用中,利用该语言开发软件并不多。所以许多人认为PASCAL只是一种教学语言。2PASCAL语言 3FORTRAN语言FORTRAN是最早出现的高级语言之一。它是针对科学计算而设计的一种高级语言(早期计算机的主要任务就是进行科学计算),到目前为止,FORTRAN仍主要是用于科学计算。C语言出现后,有人给FORTRAN语言判了死刑,但事实上,在科学计算上,尤其是在大规模科学计算上,FORTRAN仍是首选的高级语言。FORTRAN自身也在发展,目前,FORTRAN已发展到90和95版本。微机上的FORTRAN编译器也有多种,在国内最流行的是VISUALFORTRAN5.0及更高的版本。此外还有其它的编译器如NDPFORTRAN等,只是在国内不大流行。 4VISUALBASIC是由微软公司开发的,支持WINDOWS平台下开发的BASIC语言。它支持面向对象的开发,是目前WINDOWS平台下流行的开发工具之一。5其它语言在实际应用中,还有许多其它的高级语言或开发工具。一般来说,每一种高级语言或开发工具都有它的使用范围,到目前为止,还没有一种语言能包打天下。 十一学习高级语言目的对于计算机或电子专业来说,C语言的学习有这样几个目的:1学会使用C语言,能用C语言编制程序。2掌握高级语言的基本特点,为学习其它的高级语言或开发工具打下坚实的基础。同时,为后续有关课程的学习打下基础3对计算机程序的工作过程有所了解,增强使用程序的能力,而且,在学习C语言过程中所得到的能力,很容易迁移到其它软件的使用中。 第二章顺序结构程序的设计顺序结构程序是指在程序的每次执行过程中,程序中的各条语句按照在程序中的先后顺序依次执行。每个顺序结构程序中的可执行语句在每一次程序执行的过程中,执行且只执行一次。顺序程序是最简单的程序。设计一个程序,首先要将问题分析清楚,然后用适当的方法将问题描述出来,再根据问题的描述编成程序,最后调试运行。描述问题的方法很多,有各种流程图,层次图、伪代码等,更多的时候是多种手段混合使用。 例2.1最简单的C程序,这也是世界上第一个C程序。/*exam21.c*//*最简单的C程序*/#includemain(){printf(“Hello,world!”);}头文件主函数 例2.2已知圆的半径为R(R是一个可变的量),求圆的面积和周长,用计算机求解,程序如下/*exam22.c*//*计算圆的面积和周长*/#includemain(){floatr,area,s;scanf(“%f“,&r);area=3.14*r*r;s=2*3.14*r;printf(“面积=%f,周长=%f”,area,s);}1、注释2、数据说明3、数据输入4、数据处理5、结果输出 例2.3已知平行四边形的长为50,宽为10,计算平行四边形的面积和周长。程序如下://exam23.c//计算平行四边形的面积和周长#includemain(){floata,b;a=10.0;b=5.0;printf(“AREA=%f”,a*b);}程序 例2.4以我国1992年工业产值为100,如果以9%的年增长率增长计算到2000年时的工业产值。1算法分析:对此问题,要找出问题的数学模型。设r为年增长率,n为年数,v为第n年的总产值。则有v=100*(1+r)n2数据结构根据算法分析,至少要用到这么几个量,年增长率、年数、第n年的总产值。而这几个量中,年增长率肯定是小数(浮点)型数据,年数是整数(整型),总产值不会是整数,应为浮点数。这些数据都要放在相应的变量中,并要进行相应的数据说明。 //exam24.c//计算到2000年的工业产值#includemain(){intn;floatrate,value;n=2000-1992;rate=0.09;value=100*pow((float)(1+r),(float)n);printf(“2000年的产值为%f:”,value);}pow为求幂的函数格式pow(底,指数)底,指数均为浮点数。强制类型转换3源程序代码 此例中,只能算到2000年,且年增长率为9%时的工业产值。如果将工业产值改为10%,或者要算到其它年份,必须要修改源程序,使用不便。方法2:用scanf函数重新编写程序。//exam24.c//计算到2000年的工业产值#includemain(){intn,year;floatvalue,rate;printf(“请输入年份和年增长率”);scanf(“%d,%f”,&year,&rate);n=year-1992;value=100*pow((float)(1+r),(float)n);printf(“按给定利率到指定年份的产值为%f”,value);} 例2.5鸡兔同笼,已知鸡兔总头数为H(Heads),总数为F(Feet),问鸡兔各有多少只?1算法分析:(1)建立数学模型设鸡为x只,兔为y只,由题意有:x+y=h......(1)2*x+4*y=f......(2)(2)求解方程,找出x,y的具体求解公式:以下用消元法找出方程的解 (2)式-2×(1)式注意:计算机不会自己建数学模型,也不会自己解方程!2×y=f-2×hy=(f-2×h)/24×(1)式-(2)式2×X=4×H-FX=(4×H-F)/22数据结构程序中要用到不同的数据,存放头、脚数量的变量,存放方程解(鸡、兔数量)的变量,存放方程判别式的变量等。对于头、脚的数量,肯定是整型变量,方程的解理论上讲是整型,但在求解方程时要进行运算,为了避免发生错误,最好是用浮点数据。 3伪代码(由于此问题比较简单,也可直接编写程序)。说明变量x,y,f,h输入数据f,h计算x,y打印结果伪代码是一种程序设计工具,介于程序语言与自然语言之间,伪代码不能被计算机编译,但它很容易翻译成高级语言. //exam25.c#includemain(){floatx,y;intf,h;printf(“InputthenumbersofHeadsandFeet”);scanf(“%d,%d”,&f,&h);x=(4.0*h-f)/2.0;y=(f-2.0*h)/2.0;printf(“Heads=%d;Feet=%d”,h,f);printf(“Chicken=%d,rabbits=%d”,x,y);}4源程序代码说明输入数据的内容 第三章分支结构程序设计问题:1、从键盘输入一个数,如果该数为正,打印,否则不打印;2、将考试成绩不及格的学生名单打印出来;3、解一元二次方程,求出相应的实根或复根。对于上述或类似问题,需要进行某种判断,并根据不同情况进行不同的处理,怎样进行程序设计? 解决办法:1、引入新的程序结构,分支结构,有时也称判断结构或选择结构。2、为了和分支结构相配合,同时还要引入逻辑表达式的概念。3、有三种形式可进行分支结构的程序设计A、if结构B、多重选择结构(switch语句)C、无条件转移结构(goto语句) 本章内容if语句关系表达式和逻辑表达式if语句的变形及嵌套多重选择语句(switch语句)无条件转移语句(goto语句)本章小结 例3.1求一元二次方程的根1、算法分析:2、数据结构:由于问题简单,只需用到一些单精度实数 3、伪代码输入方程系数a,b,c计算判别式d=b*b-4*a*cif判别式大于等于0then{计算两个实根定位输出光标打印结果}else{计算实部计算虚部定位输出光标打印结果}endprogram 4、源程序清单#include#includemain(){floata,b,d,c,x1,x2,p,q;printf(“输入方程系数:”);scanf(“%f,%f,%f”,&a,&b,&c);d=b*b-4*a*c;if(d>=0){x1=((-b+sqrt(d))/(2*a);x2=((-b-sqrt(d))/(2*a);printf(“x1=%f,x2=%f ”,x1,x2);else{p=-b/(2*a);q=sqrt(-d)/(2*a);printf(“x1=%f+i%f,x2=%f-i%f ”,p,q,p,q);}}求平方根的函数 例3.2商店售货,按购买货物款的多少分别给予不同的优惠折扣,编程计算实际应付货款。购货不足250元,没有折扣;购货250元(含250元,下同),不足500元,减价5%;购货500元,不足1000元,减价7.5%;购货1000元,不足2000元,减价10%;购货2000元及以上,减价15%;1、算法分析:设购物款为M,折扣为D,则D可表示为:D=0(M〈250)D=0.05(250≦M<500)D=0.075(500≦M<1000)D=0.1(1000≦M<2000)D=0.15(2000≦M) 2、源程序清单//exam42.c#includemain(){floatm,d,t;printf(“请输入购物金额:”);scanf(“%f”,&m);if(m<250)d=0;if(m>=250&&m<500)d=0.05;if(m>=500&&m<1000)d=0.075;if(m>=1000&&m<2000)d=0.1;if(m>=2000)d=0.15;t=m*(1-d),//计算应付款printf(“实际应付款:%f”,t);}输入购物款:249实际应付款:249输入购物款:500实际应付款:462.5 例3.3购物折扣程序的另一个写法改写后,程序更清晰,执行时间更省。//exam42.c#includemain(){floatm,d,t;printf(“请输入购物金额:”);scanf(“%f”,&m);if(m<250)d=0;elseif(m>=250&&m<500)d=0.05;elseif(m>=500&&m<1000)d=0.075;elseif(m>=1000&&m<2000)d=0.1;else(m>=2000)d=0.15;t=m*(1-d),//应付款printf(“实际应付款:%f”,t);} 例3.4从键盘输入一字符,如果为Y,则打印是,为N,打印否,其它字符,打印输入错误。//exam44.c#includemain(){chara;printf(“输入一个字符:”);a=getchar();if(a==“Y”||a==“N”){if(a==“Y”)printf(“是”);elseprintf(“否”);}elseprintf(“输入数据不合要求”);}演示 #includemain(){intx,y,z;printf(“Iputx,y,z:”);scanf(“%d,%d,%d”,&x,&y,&z);if(x>0){if(y>0)if(z>0)printf(“所有数据大于零!”);elseprintf(“只有X,Y大于零!”);}elseif(x==0){if(y==0)if(z==0)printf(“所有数据都为零!”);elseprintf(“只有X,Y等于零!”);}elseprintf(“X小于零!”);}例3.5演示嵌套不能太深,一般以三层为限,嵌套太深,容易出错。嵌套演示程序 #includemain(){intx,y,z;printf(“Iputx,y,x:”);scanf(“%d,%d,%”,&x,&y,&z);if(x>0&&y>0&&z>0)printf(“所有数据大于零!”);elseif(x>0&&y>)printf(“只有X,Y大于零!”);elseif(x==0&&y==0&&z==0)printf(“所有数据都为零!”);elseif(x==0&&y==0)printf(“只有X,Y等于零!”);elseif(x<0)printf(“X小于零!”);}利用逻辑表达式将例4.5改写,改写后程序的结构要清晰得多。演示 例3.6将输入的字母转化为小写字母。1算法分析在计算机中,处理英文字母,其实是处理它的ASCII码值。将大写字母变成小写字母,就是将大写字母的ASCII码值变为相应的小写字母的ASCII码值。大写字母的ASCII值比小写字母的ASCII值小32,故将相应字母的ASCII值加32就变成了小写字母。程序设计时,要注意如果输入的不是大写的字母,则不用转换。 main(){charch;scanf(“%c”,&ch);if(ch>=‘A’&&ch<=‘Z’)ch=ch+32;printf(“%c”,ch);}2源程序代码之一 main(){charch;scanf(“%c”,&ch);ch=(ch>=‘A’&&ch<=‘Z’)?(ch+32):ch;printf(“%c”,ch);}注意:1条件运算符优先于赋值运算符,低于关系运算符;2条件运算符的结合方向为“自右至左”a>b?a:c>d?c:da>b?a:(c>d?c:d)此处使用了条件运算符3源程序代码之二 #includemain(){inti;printf(“输入数字1-7:”);scanf(“%d”,&i);if(i==1)printf(“TodayisMonday! ”);elseif(i==2)printf(“TodayisTuesday! ”);elseif(i==3)printf(“TodayisWednesday! ”);elseif(i==4)printf(“TodayisThursday! ”);elseif(i==5)printf(“TodayisFriday! ”);elseif(i==6)printf(“TodayisSaturday! ”);elseprintf(“TodayisSunday! ”);}例3.7输入星期中的某一天,显示对应的英文此例似显复杂,是否有更好的办法? main(){intI;printf(“输入数字1-7:”);scanf(“%d”,&i);switch(i){case1:printf(“TodayisMonday!”);case2:printf(“TodayisTuesday!”);case3:printf(“TodayisWednesday!”);case4:printf(“TodayisThursday!”);case5:printf(“TodayisFriday!”);case6:printf(“TodayisSaturday!”);default:printf(“TodayisSunday!”);}}例3.8用switch语句重写打印星期程序。main(){intI;printf(“输入数字1-7:”);scanf(“%d”,&i);if(i==1)printf(“TodayisMonday!”);elseif(i==2)printf(“TodayisTuesday!”);elseif(i=3)printf(“TodayisWednesday!”);elseif(i==4)printf(“TodayisThursday!”);elseif(i==5)printf(“TodayisFriday!”);elseif(i==6)printf(“TodayisSaturday!”);elseprintf(“TodayisSunday!”);} 例3.9用switch语句重写例4.4的程序main(){chara;printf(“输入一个字符:”);a=getchar();if(a==“Y”||a==“N”)if(a==“Y”)printf(“是 ”);elseprintf(“否 ”);elseprintf(“字符不合要求! ”)}显然,改写后的程序更清晰,更简短。main(){chara;printf(“输入字符:”);a=getchar();switch(a){case‘Y’:printf(“是 ”);break;case‘N’:printf(“否 ”);break;default:printf(“字符不合要求! ”)}} 例3.10在屏幕上显示菜单分析:程序首先在屏幕上将功能显示出来,用户根据需要选择相应的功能,程序根据用户的选择执行相应的程序段。对于这类程序,往往是用户输入一个英文字母,然后程序根据字母来断别程序该执行什么程序段,故一般用switch语句比较方便。 2源程序清单switch(a){case'i':printf(“调用输入模块!”);break;case'o':printf(“调用输出模块!”);break;case'l':printf(“调用查找模块!”);break;case‘q':printf(“调用退出模块!”);break;}}main(){intchara;printf(“请选择:”);printf(“输入i”);printf(“输出o”);printf(“找出最长的单词l”);printf(“退出q”);a=getchar();执行选项时,要调用相应的模块(程序段)。由于现在还没有相应的程序,暂用一条打印语句代替。以后会用相应程序代替 例3.11统计某班级中计算机课程中考试成绩高于80分的人数。假设学生人数为10main(){inti,fs,j=0;Loop:scanf(“%d”,&fs);j++;//统计已读入的数据个数if(fs>80){i++;printf(“%d,%d”,i,fs);}if(j<10)//当数据没读完时,继续gotoLoop;} 本章小结1、分支结构:改变程序的执行流程,或有选择地执行程序;2、if~else和switch是结构化语句,而goto语句不是;3、块if语句可以取代所有的分支结构,而多重分支结构不可;4、块if语句中的条件可以是复杂逻辑表达式,而多重分支中的条件只能是简单表达式;5、块if与goto语句结合,可以构造任何复杂的循环,但用专门的循环语句更方便。返回目录 第四章循环结构在人们所需处理的运算任务中,常常需要用到循环结构,所谓循环是指对同一个程序段重复执行若干次。被重复执行的部分(由若干语句组成)称为循环体。循环结构又称重复结构。本章主要内容:while循环语句do~while循环语句for循环语句循环嵌套及注意的问题程序的流程图 例4.1查找某班级中计算机课程中考试是否有成绩高于95分的人。1算法分析此题的算法比较简单,将该班计算机课程的考试成绩输入计算机,每输入一个成绩,就与95进行比较,如果有大于95的成绩,程序结束。但在输入和比较时,要有一种结构,使得计算机能不停地执行输入和比较,当某种条件(如找到了大于95分的成绩,或者所有的成绩都已输入完)满足,则结束程序。2数据结构数据结构也很简单,用一个变量存放输入的数据,还要用一个变量记录输入数据的个数,如果输入的数据完成时,则该变量就等于输入的数据的最大个数。 说明整型变量fs,i;输入成绩fs;记录输入成绩的个数(i=i+1);iffs>95或者输入数据完成(i>某值){退出输入;elsego输入成绩行;}if(fs>95)打印字符串“找到了!”else打印字符串“没找到!”结束程序3程序伪代码 main(){intfs,i;i=1;loop1scanf(“%d”,&fs);i=i+1;if(fs>95||i>10){gotoloop2;elsegotoloop1;}loop2if(fs>95)printf(“FIND!”);elseprintf(“DONOTFIND!”);}4源程序代码之一 5源程序代码之二#includemain(){intfs,i=1;printf(" 请输入第%d个数据:",i);scanf("%d",&fs);while(fs<=95&&i<10){printf("请输入第%d个数据:",i+1);scanf("%d",&fs);i++;}if(fs>95)printf(" 找到啦! ");elseprintf(" 没找到! ");}注意输入提示 例4.2将输入的正文复制到输出,每次一个字符.1算法分析本题的含义是每次从键盘上输入一个字符,然后将该字符在屏幕上输出。因为是正文,故其结束的标志为ctrl+z要使用字符函数来接受从键盘输入的字符,一般用getchar函数。 2源程序代码#includeintmain(void){charc;printf(“inputatextendofctrl+z ”);c=getchar();while(c!=EOF){putchar(c);c=getchar();}}在头文件中定义的常数(-1) #includeintmain(void){charc;printf("inputatext ");while((c=getchar())!=EOF)putchar(c);}例4.3请阅读下面的程序,说明程序的功能。该程序与上面的程序功能一样,只不过将字符输入语句放入循环判断条件中。 例4.4比较两种循环的用法。从键盘输入一个字符Y或N,直到输入正确为止。1算法分析在程序中,经常会遇到要求用户输入Y或N,以确定某种状态。为防止用户在无意中碰到键盘而输入错误的字符,程序对用户输入的不合要求的字符忽略,并要求用户重新输入。为了做到上述要求,程序对输入的字符要进行判断,如果不合要求,则将输入的字符忽略,并等待用户重新输入。显然,要做到这一点,须用一个循环程序,当输入正确时,循环结束。设输入变量为c,则循环控制条件为!(c==‘Y’||c==‘N’)或者(c!=‘Y’&&c!=‘N’) 2源代码比较格式1:c=getchar();while(c!=’Y’&&c!=’N’){printf("answerisYorN“);c=getchar();}格式2:do{printf(”EnterYorN“);c=getchar();}while(c!=’Y’&&c!=’N’);显然,格式2更方便! #includemain(){intfs,i=1;do{printf("请输入第%d个数据:",i);scanf("%d",&fs);i++;}while(fs<=95&&i<10);if(fs>95)printf(" 找到啦! ");elseprintf(" 没找到! ");}例4.5将例4.1用新的do~while语句重写查找某班级考试结果是否有高于95分的成绩。 例4.5统计某班级中计算机课程中考试成绩高于80分的人数,现用for循环语句重写该程序。main(){intfs,sum,j;sum=0;for(j=1;j<=10;j++){scanf(“%d”,&fs);if(fs>80){sum=sum+1;printf(“sum=%d “,sum);}}}给循环变量j赋初值1循环变量j加1循环控制条件 例4.6求数列的和。1、分析:该数列中每一项的分母都有规律,后一项为前一项加5,分母的通式为:An=An-1+5,A1=1或:程序 //exam53.c#includemain(){inti;floatsum=0;for(i=0;i<=100;i++){sum=sum+1.0/(1+i*5);}printf("%f",sum);}sunhui:2002年4月10日星期三6时16分56秒根据昨天上机的情况,本次课安排了一次习题课。昨天上机时也布置了两道题。一道题是求和:加一道题是教材120页的第八题。学生的思路还是很多的。 例4.7求的近似值,精确到公式为:此类问题相对简单,但要注意累加变量和通项的初值。该问题中,累加变量sum的初值为0,通项term的初值为1。 #include#includemain(){doublepi,term,sum;inti;sum=0.0;term=1.0;for(i=1;term>0.00000000000001;i++){term=1.0/(i*i);sum=sum+term;}pi=sqrt(sum*6);printf("%f ",pi);}两个相乘,其值会溢出,故得不到想要的结果 例4.8分别统计某班学生各科成绩中高于80分的人数。学生人数为30人,课程为:数学、语文、英语、物理、生物。intn,i;n=0;for(i=1;i<=30;i++){//对学生人数循环scanf(“%d”,&fs);if(fs>=80)n=n+1;//累加大于80分的人数}printf("n=%d“,n);先看统计数学成绩 统计五门课程,只需将统计一门课程的程序用不同的数据执行5次,这可用一个循环实现。//统计某一门课程n=0;for(i=0;i<3;i++){scanf(“%d”,fs);if(fs>=80)n=n+1;}printf("n=%d“,n);for(j=0;j<5;j++){//对课程循环}统计每门课程的成绩!//统计一门课程n=0;for(i=0;i<3;i++){scanf(“%d”,fs);if(fs>=80)n=n+1;}printf("n=%d“,n);这里,用到了二重循环。 例4.9求自然对数的底e,取前n项的和。1算法分析此题有两种算法:一种是直接求通项,则先求n!,然后求1/n!,这种方法比较简单。求n阶乘的程序段为f=1;for(j=1;j<=n;j++)f=f*j; ,则有设通项为由于数列的第一项不能用通项表示,故将其直接放入累加变量中。程序片段为第二种求通项的公式e=1.0;a=1;for(m=1;m<=n;m++){a=a*(1/m);e=e+a;} //利用双重循环计算main(){inti,j,n;floate,f;printf(“PleaseinputN:”);scanf(“%d”,&n);e=1.0;for(i=1;i<=n;i++){f=1;for(j=1;j<=i;j++)f=f*j;e=e+1/f;}printf(“e=%f”,e);}//利用单重循环计算main(){inti,n;floate,f;printf(“PleaseinputN:”);scanf(“%d”,&n);e=1.0;f=1;for(i=1;i<=n;i++){f=f*(1/i);e=e+f;}printf(“e=%f”,e);}2源代码 例4.10从键盘上输入字符并按行显示,遇到Esc键结束,要求只显示输出内容。在每行前显示字符串“TheLineNo.isn”,n为1,2,3,1算法分析此题要求从键盘上输入字符并按行显示,初一看,只要用行输入语句就行。但仔细分析后会发现,用行输入语句不行。如用gets或scanf语句,则输入的内容会在屏幕上回显(即输入的内容显示在屏幕上),其次,这两个语句都不能单独捕捉到Esc键。因此,只能用单个字符输入的函数,但要求不要回显,getchar函数也不行,但推测是与getchar相类似的函数。故可先调出getchar的帮助,然后看SeeAlso。 既然是按行显示,用字符输入函数,每输入一个字符,立刻在屏幕上显示出来。当接受到换行符时,转入下一行。因此,对每一行的输入,要用一个循环,循环的控制条件就是输入的字符不为换行符。当输入为换行符时,循环从头开始。程序的结束是以接受到Esc键为标志的,故而要在行输入的外面再增加一个循环,此循环的结束条件是输入的字符为Esc键。行输入的程序片段为printf(" TheLineNo.is%d",i);do{printf("%c",c);c=getch();}while(c!=13&&c!=27);13return键ASCII码27Esc键ASCII码getch不回显输入字符函数 #include#includeintmain(void){inti=0;charc=' ';while(c!=27){c=' ';i++;printf(" TheLineNo.is%d",i);do{printf("%c",c);c=getch();}while(c!=13&&c!=27);}printf(" Theend ");}从键盘上输入一个字符,不回显Esc键的ASCII码值回车符的ASCII码值2源程序代码 例4.11输入一个算式,模拟袖珍计算机的加、减、乘、除四则运算。例如:输入10.8+0.13*100计算结果为:1093.000000假定计算时不考虑运算符的优先级,也不允许有(),而是按照运算符出现的先后顺序执行运算。 main(void){doublex,y;charop;inx:printf("inputarithmeticexpression: ");scanf("%lf",&x);while((op=getchar())!=' '){iny:scanf("%lf",&y);switch(op){case'+':x+=y;break;case'-':x-=y;break;case'*':x*=y;break;case'/':if(y)x/=y;else{printf("divisioniszero,inputyagain ");gotoiny;}break;default:printf("illegaloperator,inputagain ");gotoinx;}}printf("%8.4f ",x);} 开始输入系数a,b,cd=b*b-4*a*cd>=0例4.12:用流程图描述求解一元二次方程的根x1=(-b+sqrt(d))/2x2=(-b-sqrt(d))/2输出x1,x2p=-b/2q=sqrt(-d)/2输出x1,x2结束TF 例4.13输入一段C语言程序(一个正文),按原来格式复制到输出,复制过程中删除输入程序中所有的注释。此处注释专指“/**/”的注释。1算法分析程序先要输入,在C语言中,当从键盘输入时,先将输入数据放入键盘缓冲区中,当接受到回车符后,程序才开始从键盘缓冲区中读数据。分析之前,首先,弄明白问题的意思。程序要求输入一段程序(C语言),并将输入的程序在屏幕上输出。在输出时将程序中的注释(/**/)过滤,既不输出注释。 输出时要将注释过滤掉,首先要判别什么内容是注释内容。例:while(a>b){/*whilea>b*/printf(“*/”);}字符的类型,仅从字符本身是无法区分的。如上面的串中,字符串while可能是普通字符串,也可能是注释字符串。同样,对“*/”字符,与/*配对是注释字符,否则就是普通字符。因此,需要一个类型标志state。因此,字符的类型标志可取为:普通字符(COPY)、注释开始(START)、注释字符(COMMENT)、注释结束(END)。因此,在C程序中,从注释的角度来看,字符共有四种,普通字符、注释开始字符、注释结束字符及注释字符。while(a>b){printf(“*/”);}为普通字符,whilea>b为注释字符,/**/为注释标志字符。 参照下例开始程序设计。开始时,state置普通字符状态。在普通字符状态,如果输入字符不是‘/’,则将字符原样输出,如果输入字符为‘/’,在开始注释状态,如果输入字符为‘*’,while(a>b)/*whilea>b*/printf(“*/”);则可能开始注释,将state设为注释状态,字符‘/’不输出; 参照下例开始程序设计。开始时,state置普通字符状态。在普通字符状态,如果输入字符不是‘/’,则将字符原样输出,如果输入字符为‘/’,在开始注释状态,如果输入字符为‘*’,表明注释开始,state置为注释状态;如果输入字符不是‘*’,while(a>b)/*whilea>b*/printf(“*/”);则可能开始注释,将state设为注释状态,字符‘/’不输出; 参照下例开始程序设计。开始时,state置普通字符状态。在普通字符状态,如果输入字符不是‘/’,则将字符原样输出,如果输入字符为‘/’,在开始注释状态,如果输入字符为‘*’,表明注释开始,state置为注释状态;如果输入字符不是‘*’,表明前面设置的注释开始状态不对,state改为普通字符状态;然后将‘/’输出,接下将刚输入的字符原样输出。在注释状态,如果输入的字符不是‘*’,字符不加处理,否则,表明注释将要结束,将state置为结束状态。在结束注释状态,如果输入字符为‘/’,表明注释结束,state置为普通字符状态;否则,仍为注释状态,state置为注释状态。while(a>b)/*whilea>b*/printf(“*/”);则可能开始注释,将state设为注释状态,字符‘/’不输出; while((c=getchar())!=EOF){if(输入换行符)输出换行符elseswitch(state){普通字符状态:if(输入字符‘/’)字符状态设为START;else字符原样输出开始注释状态:if(输入字符‘*’)注释内容从此后开始,状态设为注释else’/’为普通字符原样输出'/'原样输出输入的字符将字符状态重新设为普通字符(COPY)注释状态:if(输入字符‘*’)注释即将结束,状态设为END;结束状态:if输入字符'/‘注释结束,状态设为COPYelse仍为注释状态}} S输出c输出’ ’Fc=‘ ’c=‘/’S=START输出‘/’输出cc=‘*’S=COMMc=‘/’S=COPYS=COMMc=‘*’S=ENDS=COPYTCOPYSTARTCOMMEND输入字符赋给c,c!=EOF循环体底部TTTTFFFFs=COPY循环流程图演示 #include#defineCOPY0#defineSTART1#defineCOMMENT2#defineEND3intmain(void){charc;intstate;state=COPY;printf("inputCprogramendofcrtl+z: ");while((c=getchar())!=EOF){if(c==' ')putchar(' ');elseswitch(state){caseCOPY:if(c=='/')state=START;elseputchar(c);break;caseSTART:if(c=='*')state=COMMENT;else{putchar('/');putchar(c);state=COPY;}break;caseCOMMENT:if(c=='*')state=END;break;caseEND:state=(c=='/')?COPY:COMMENT;break;}} 返回例4.14利用getch函数,编写密码输入程序。输入密码时,屏幕上不显示输入内容,而代之以“星号”之类的内容,以防他人看到密码。#include#includemain(){charc=' ';printf("请输入密码: ");while(c!=13){c=getch();putchar('*');}putchar(' ');} 一、函数过程的概念过程是现代过程式语言的重要概念。在程序设计中,离开了过程就谈不上结构化程序设计。过程是程序设计中的一个逻辑单元,是一段程序,能完成某一项简单的功能。过程与程序的关系类似于机器中的零部件与机器之间的关系。如系统提供的sin(x)函数,就是一个函数过程。它是系统提供的求变量(参数)X的正弦值的过程。系统中有许多这样的过程,如sqrt,strcat等。第五章函数 二、过程的优点:1、利用过程可以把程序划分为若干个独立的逻辑单元,每一个单元相对于整个程序小得多,故其调试更容易;2、过程可作为一个功能模块而被同一程序中的不同地方引用,也可被不同程序引用。如系统提供的函数,就可被不同的使用者引用。三、C语言中的过程C语言只提供了一种过程,即函数过程,也简称函数。后面,我们将把函数过程简称为函数。 例5.1编程计算组合数。设有j,k,且j>k,则组合数c为c=(j!-k!)/k!。!为阶乘的符号。1算法分析此题不难编程,只要分别求出二个数的阶乘,然后根据公式进行计算即可。求一数j的阶乘的程序段为jc=1;for(i=1;i<=j;i++)jc=jc*i; 2源程序代码之一#includemain(){inti,j,k,ic,jc,kc;floatresult;scanf("%d,%d",&j,&k);jc=1;for(i=1;i<=j;i++)jc=jc*i;kc=1;for(i=1;i<=k;i++)kc=kc*i;result=(jc-kc)/(float)kc;printf("theresult=%f ",result);} 3源程序代码之二#includefloatjc(int);main(){inti,j,k,ic,jc,kc;floatresult;scanf("%d,%d",&j,&k);result=(jc(j)-jc(k))/jc(k);printf("theresult=%f ",result);}floatjc(intn){inti;floatj=1.0;for(i=1;i<=n;i++){j=j*i;}returnj;}函数定义函数说明参数返回值问题:在主函数和函数jc中都定义了变量j,它们之间的关系如何? 例5.2编写程序,利用递归调用方式,计算n!。递归公式总是有一个结束条件,这个结束条件称为”边界条件”,如n!的边界条件为n<=1.本例如定义的函数过程名为fact,则递归调用公式可用下式表示:fact(n)=1(n=1)fact(n)=fact(n-1)*n(n>1)C=计算n!可以用下式定义:n!=n×(n-1)×(n-2)×(n-3)×‥‥×2×1 intfact(n){intf;if(n<=1)f=1;elsef=fact(n-1)*n;returnf;}#includeintfact(int);main(){inti,n;scanf("%d",&n);i=fact(n);printf("n!=%d",i);} 例5.3编写程序,利用递归调用方式计算两个数m、n的最大公约数。(其中m>n,且n≠0)分析:本例采用辗转相除法来计算两个数m,n的最大公约数。过程:m除以n,如余数r=0,则n就是最大公约数。如r≠0,则m←n,n←r,再重复上述过程,直到r=0为止,这时的n就是要求的最大公约数.例:m=112,n=77被除数除数商余数112/7713577/352735/750因此,112和77的最大公约数为7本问题的特点是要多次处理同一个问题,只是处理所用数据不同,且后一次所用数据为前一次处理的结果,正好用递归。 运行结果:m,n=?112,77gcd=7//exam63.c#includeintfgcd(int,int);main(){intm,n,g;printf(“m,n=?”);scanf("%d,%d",&m,&n);if(n>m){printf("inputerror!");return1;}g=fgcd(m,n);printf(“gcd%d ",g);return0;}fgcd(m,n){intg;if(n==0){g=m;returng;}elsefgcd(n,m%n);} 例5.4设口袋中放着12个球,其中3个是红的,3个是白的,6个是黑的,每次从中取8个球,问有多少种取法。1算法分析:(1)在袋中取出红球的可能性是0,1,2,3;白球的可能性是0,1,2,3;黑球的可能性是2,3,4,5,6;(2)如果红白球之和小于2个,则凑不满8个,故每次红白球之和至少应为2个;(3)设红球分别取0,1,2,3个(即对红球的可能取法循环),白球可能的取法也是0,1,2,3,但同时必须满足红白球之和不少于2,黑球的个数为8-红-白;(4)每找到一种组合,都将它们的组合形式打印出来。 2数据结构设计:用三个整数表示红、白、黑三种球每次取出的个数,一个整数用来记录取法。r,w,b,sum 3伪代码:所有变量均说明为整型变量sum=0(计数变量初始化)beginloop(red=0to3)(对红球的可能取法循环)beginloop(white=0to3)(对白球可能的取法循环)ifred+white<2(红球+白球<2)则本次取不到8个球,不计数elsesum=sum+1(计数)endifendloop(循环结束)endloop(循环结束)print"sum=";sum(打印结果)结束程序程序 //exam64.c#includeintpri(int,int);main(){intr,w,sum=0,i=1;for(r=0;r<=3;r++)for(w=0;w<=3;w++){if(w+r>=2){sum=sum+1;printf("No%2d",i);pri(r,w);i++;}}printf(" sum=%d ",sum);}pri(intr,intw){intb,i;b=8-r-w;for(i=1;i<=r;i++)printf("红");for(i=1;i<=w;i++)printf("白");for(i=1;i<=b;i++)printf("黑");putchar(' ');} 例6.1从键盘中输入3个数,并将其排序。1算法分析当数据较少时,问题比较简单。设3个数分别存放在变量a,b,c,假设从小到大排序,则步骤如下:第一步将a与b比较,如果a大于b,则将a,b的数据交换,然后将a与c比较,如果a大于c,则a与c交换,至此,a中的数据最小。第二步将b和c比较,如果b大于c,则b与c的数据交换。最后,得到排好序的数据。第六章数组 #includemain(){inta,b,c,t;scanf("%d,%d,%d",&a,&b,&c);if(a>=b){t=a;a=b;b=t;}if(a>c){t=a;a=c;c=t;}if(b>c){t=b;b=c;c=t;}printf("%d,%d,%d ",a,b,c);}问题如果有4、5个数据或更多的数据,程序怎样设计?显然,要有其它的解决办法。 #includemain(){inta[3],t;scanf("%d,%d,%d",&a[0],&[1],&a[2]);if(a[0]>=a[1]){t=a[0];a[0]=a[1];a[1]=t;}if(a[0]>a[2]){t=a[0];a[0]=a[2];a[2]=t;}if(a[1]>a[2]){t=a[1];a[1]=a[2];a[2]=t;}printf("%d,%d,%d ",a[0],a[1],a[2]);}在此例程序中,完成的功能与前面的程序一样,但存放数据用了一个新类型—数组。数组,顾名思义,就是一组数据,但用一个相同的名字表示。这组数据中各个元素的区分依赖数组的下标。下标就是数组名后方括号中的数字。这种处理方式类似于数学中的下标,如 以字符数组为基础做一个简单的文本编辑程序。该程序首先从键盘中最多输入100行字符,每行最多80个字符然后将输入的字符在屏幕中输出。当输入不足80行时,输出空行表示输入结束。例6.2阅读程序时,要注意C语言的特点,程序中利用了一些C语言中常用的技巧。 1算法分析此例涉及到字符数组,一般情况下,字符程序设计比较困难。首先,要有一个变量能存放输入的字符文本,到目前为止,能用的工具只能是存放字符串的数组。其次,要能输入字符串的函数,第三,用什么方式能达到在输入空行时结束输入。所谓空行,是指串中没有内容,也就是说,串的第一个字符即为串结束符。因此,判断一个串是否是空行的标准就是判断串的第一个字符的ASCII值是否为零。2数据结构存放文本的数组要求一个二维数组,数组的类型为字符型,文本最大为100行,每行最长为80个字符,还要一个位置存放串结束符,所以行的长度应定义为81,即数组有81列,由此得到的二维数组为chartext[100][81]。 当遇到串结束标志符’ ’时,其ASCII值为零,循环条件为假,结束循环.当输入空行,该行的第一个字符text[t][0]为串结束符’ ’,而字符 的ASCII值为零,!( )即为非零,表示真,执行break语句,退出输入循环.#include#defineMAX100#defineLEN81chartext[MAX][LEN];main(){intt,i,j;for(t=0;t#includeintstrcat1(char[],char[]);main(){charc1[10],c2[10];gets(c1);gets(c2);strcat1(c1,c2);puts(c1);}strcat1(chars1[],chars2[]){intj=0,i=0;while(s1[i++]);i--;while(s2[j])(s1[i++]=s2[j++]);s1[i]=' ';}//找到串结束符的位置//将串s2追加到s1中注意:stract1函数中用到串结束符,函数不进行数组越界检查。也就是说,当数组C2中的串和数组C1中的串的长度之和超过数组C1的大小时,C1中存放的串将没有串结束符,由此产生的后果是对C1中的串的运算结果不可知。 intstrcat1(char[],char[],int);main(){intlen;charc1[10]={'N','a','n',' '},c2[10]={'c','h','a','n','g',' '};len=sizeof(c1);if(strcat1(c1,c2,len))exit(1);puts(c1);}strcat1(chars1[],chars2[],intlen){intj=0,i=0;while(s1[i++]);i--;while(s2[j]){(s1[i++]=s2[j++]);if(i>=len){puts("arrayoverflow ");return1;}}s1[i]=' ';return0;}例6.4字符追加函数,检查数组越界。退出过程 intstrcmp1(char[],char[]);main(){charc1[5]={'n','a','n',' '};c2[]={‘n’,’a’,’n’,’ ’};printf("%d ",strcmp1(c1,c2));}strcmp1(chars1[],chars2[]){//字符串比较函数inti=0;while(s1[i])if(s1[i]-s2[i])return(s1[i]-s2[i]);elsei++;return0;}例6.5字符串比较函数的实现方法之一此处要求第1个串比第2个串长,考虑一般情况,则要用长的串放入循环中 数组应用程序例一排序例6.6从键盘上输入4个数,并存入数组中,然后找出其中最大的数并打印其值。#includemain(){inttemp,i,j,a[4];scanf("%d,%d,%d,%d",&a[0],&a[1],&a[2],&a[3]);for(i=1;i<4;i++)if(a[0]main(){inti,j,x,n,a[]={13,15,7,10,20,8,4,19};n=sizeof(a)/4;for(i=1;i=0&&a[j]>x){a[j+1]=a[j];j--;}a[j+1]=x;}for(i=0;i=1){for(i=d;i=0&&a[j]>x){a[j+d]=a[j];j=j-d;}a[j+d]=x;}d=d/2;}} 例6.10棋盘游戏人和计算机下对角棋,为简单起见,只下三个棋子。程序演示#defineSPACE''#include#includevoiddisp_matrix();voidget_player_move();voidget_computer_move();charcheck();charmatrix[3][3]={SPACE,SPACE,SPACE,SPACE,SPACE,SPACE,SPACE,SPACE,SPACE}; main(){chardone;printf("ThisisthegameofTicTacToe. ");printf("Youwillbeplayingagaistthecomputer. ");done=SPACE;do{disp_matrix();get_player_move();done=check();if(done!=SPACE)break;get_computer_move();done=check();}while(done==SPACE);if(done=='X')printf("Youwon! ");elseprintf("Iwon!!! ");disp_matrix();} voidget_player_move(){intx,y;printf("EntercoordinatesforyourX,Y:");scanf("%d,%d",&x,&y);x--;y--;if(matrix[x][y]!=SPACE){printf("Invalidmove,tryagain. ");get_player_move();}elsematrix[x][y]='X';} voiddisp_matrix(){intt;for(t=0;t<3;t++){printf("%c|%c|%c",matrix[t][0],matrix[t][1],matrix[t][2]);if(t!=2)printf(" --|--|-- ");}printf(" ");}voidget_computer_move(){intt;char*p;p=(char*)matrix;for(t=0;*p!=SPACE&&t<9;t++)p++;if(t==9){printf("draw ");exit(0);}else*p='0';} charcheck(){intt;char*p;for(t=0;t<3;t++){p=&matrix[0][t];if(*p==*(p+1)&&*(p+1)==*(p+2))return*p;}for(t=0;t<3;t++){p=&matrix[0][t];if(*p==*(p+3)&&*(p+3)==*(p+6))return*p;}if(matrix[0][0]==matrix[1][1]&&matrix[1][1]==matrix[2][2])returnmatrix[0][0];if(matrix[0][2]==matrix[1][1]&&matrix[1][1]==matrix[2][0])returnmatrix[0][2];returnSPACE;} 例6.11编写一函数,由实参传来一个字符串,统计此字符串中的字母、数字、空格和其它字符的个数,在主函数中输入字符串以及输出上述结果。 //exam611.c#includeintsum(char[],int[]);main(){chars[100];inta[4]={0,0,0,0};gets(s);sum(s,a);printf("%d,%d,%d,%d",a[0],a[1],a[2],a[3]);}sum(chars[],inta[]){inti=0;while(s[i]){if(isalpha(s[i]))a[0]++;elseif(isdigit(s[i]))a[1]++;elseif(isspace(s[i]))a[2]++;elsea[3]++;i++;}} 例6.12写一函数,输入一行字符,将此字符串中最长的单词输出。1算法分析:此例看似简单,其实不然。首先,要确定单词的定义。在此,我们规定,单词是两个分隔符中间的一个连续的英文字母序列。分隔符:除英文字母外的所有字符,如空格,各种标点符号,数字等。例Thereisalongwordatthestring. 程序设计的思路:程序中用一个数组L保存到目前为止所取出的最长的单词,用数组T存放每次从串中取出的单词。从串中取出一个单词,将其放入数组T中,并将数组T中的单词与数组L中的单词比较长度,如果数组T中的单词比数组L中的单词长,则将数组L中的单词换成数组T中的单词。当字符串读完时,数组L中存放的将是最长的单词。 2数据结构字符数组S[81]用来存放从键盘上读入的字符串;字符数组word[20]用来存放到目前为止最长的单词;字符数组word1[20]用来存放每次取出的单词。 chars[81],word[20],word1[20];inti=0,j=0;do{if(s[i]为英文字母)将该字母拷贝到word1;else{已取到一个单词将word1加上串结束符,以便能用串函数if(strlen(word1)>strlen(word))strcpy(word,word1);将word1中内容清除,以便存放新取出的单词}}while(s取下一个字符);} //exam612a.c#include#includemain(){chars[100],//存放输入的字符串word[20]={‘ ’};//存放最长的单词gets(s);sum(s,word);//取得最长的单词puts(word);}源程序代码一 sum(chars[],charlong[]){inti=0,j=0;chartemp[20]={' '};do{if(isalpha(s[i]))temp[j++]=s[i];else{temp[j]=‘ ’;//给取到的单词加上串结束符,//以便能用串函数if(strlen(temp)>strlen(long))strcpy(long,temp);j=0;//使在取下一个单词时temp从头开始}}while(s[i++]);} //exam612b.c#include#includemain(){chars[100];inti,len[2];gets(s);sum(s,len);for(i=len[1];ilen[0]){len[0]=lentemp;len[1]=postemp;}lentemp=0;postemp=i+1;}}} 例6.13用递归法将一个整数n转换成字符串,整数的长度不定。//exam714.c#include#includevoidchars1(char[],char[]);voidchars(int,char[]);main(){intn;chars[10],s1[10];scanf("%d",&n);chars(n,s1);//得到与原数字顺序相反的字符串chars1(s,s1);//将字符串顺序倒过来puts(s);} /*递归函数用整除10的方式取整数n的个位,每取一次,n被整除10,当n为零时,整数被取完。但所取的字符顺序与原数字的顺序相反。*/voidchars(intn,chars[]){staticinti=0;//i记录字符的位置,递归过程中连续使用intsum;if(n){sum=n;n=sum/10;s[i++]=(sum-n*10)+48;//取得数字,并化成字符//的ASCII码chars(n,s);}elses[i]=' ';} //将字符串倒转voidchars1(chars[],chars1[]){intlen,i=-1;len=strlen(s1);while(s1[++i])s[i]=s1[len-i-1];s[i]=' ';} 例6.14用非递归法重写例6.13相应的程序//exam7151.c#includevoidchars(int,char[]);main(){intn;chars[10];scanf("%d",&n);chars(n,s);puts(s);} voidchars(intn,chars[]){inti=0,sum,len;chars1[10];while(n){//不断将n整除10,当n为零时,//已取出n中的每一位数。sum=n;n=sum/10;s1[i++]=(sum-n*10)+48;}s1[i]=' ';len=strlen(s1);i=-1;while(s1[++i])s[i]=s1[len-i-1];s[i]=' ';} 例6.15用牛顿迭代法求非线性方程的根已知方程为:牛顿迭代法为 //用牛顿迭代法求方程的根(*x*x*x+b*x*x+c*x+d=0)//exam615.c#include#includemain(){intjs,k;doublex1,x2,f,ff,eps=1;x2=1.0;while(eps>0.00000001){x1=x2;f=x1*x1*x1+2.0*x1*x1+3.0*x1+4.0;ff=3*x1*x1+4.0*x1+3.0;x2=x1-f/ff;eps=fabs(x2-x1)/fabs(x2);}printf("root=%f ",x2);} 例6.16用递归方法求n阶勒让得多项式的值.//exam716.c#includefloatp(int,float);main(){intn=4;floatx=1,y;y=p(n,x);printf("%f",y);} p(intn,floatx){floatf;if(n==0)f=1;elseif(n==1)f=x;elsef=((2*n-1)*x-p(n-1,x)-(n-1)*p(n-2,x))/(float)n;returnf;} 例6.17给出10个职工的姓名及编号,用函数实现以下功能:1将编号从小到大排列,相应的职工姓名同时调整2输入职工编号,用折半查找法找到相应的职工姓名,并输出。此问题比较典型,在编写程序之前,先要进行问题的分析。 解题思路:本问题是实际数据库问题的雏形。程序大致可分成主函数,排序函数,查找函数。有几个算法要解决:排序算法:问题要求排序,故要选择排序算法,此问题我们选择简单的插入排序算法。数据结构:本问题的数据结构设计很重要。不同的数据结构设计,涉及到程序的实现。 问题中有两组数据,职工编号和职工姓名。在没学结构之前,只能用数组来实现这两种组数据的存储。可以将编号和姓名分别用两个数组来存储。对于编号,可用整型数组,也可用字符数组,本题用整型数组,定义如下:intbx[];对于姓名,每一个姓名是一个字符串,按一般的规定,姓名不能超过4个汉字,加上串结束符,共9个字符。如果用一个二维数组来存储姓名,则每行的长度为9,故二维数组可设计为:charname[][9]还有一点要注意,编号和姓名是对应的,在两组数据中,也应有对应关系,在本题中,用下标来对应。即两数组元素的下标相同,则它们有对应关系。 //exam617.c#include#includemain(){intn,i,bx[10]={7,8,1,2,10,9,3,4,5,6};charname[10][9]={{"钱七"},{"周八"},{"吴一"},{"郑二"},{"洪十"},{"陈九"},{"张三"},{"李四"},{"王五"},{"赵六"}};sort(bx,10,name);for(i=0;i<10;i++)printf(" bx=%d,name=%s",bx[i],name[i]);putchar(' ');printf("请输入要查找的职工编号:");scanf("%d",&n);search(bx,n,10,name);} sort(intbx[],intn,charname[][9]){inti,j,x,k;charc[9];for(i=1;i=0&&bx[j]>x){bx[j+1]=bx[j];strcpy(name[j+1],name[j]);j--;}bx[j+1]=x;strcpy(name[j+1],c);}}//数组的插入排序程序for(i=1;i=0&&a[j]>x){a[j+1]=a[j];j--;}a[j+1]=x;} search(intbx[],intbxs,intmax,charname[][9]){intmin=0,mid,k;mid=(max+min)/2;while(1){if(bxs==bx[mid])break;if(bxs>bx[mid])min=mid;elseif(bxsmain(){floatx,y,*p;y=4.5;p=&x;*p=3;printf(“x=%f,y=%f”,*p,y);}运行结果:x=3.0,y=4.5将变量x的地址存入指针变量p中将指针所指地址中的内容取出将数字3存入指针所指的地址 注意:在使用指针时一定要注意指针类型的配匹,否则,后果极为严重。#includemain(){floatx,y;int*p;x=3.0;y=4.5;p=&x;printf(“x=%f,y=%f”,*p,y);printf(“x=%d,y=%f”,*p,y);}将单精度数的地址赋给指向整数的指针变量,结果不可知。如果用c编译器,只给出警告,用c++编译器会给出错误提示。输出结果不可知。 四、指针表达式1指针赋值指针可以象一般的变量一样赋值。#includemain(){inta,b,*p1,*p2;p1=&a;p2=&b;*p1=4;b=5;p2=p1;printf(“%d,%d”,*p1,*p2);}输出结果:4,4使指针p2和指针p1指向同一个整型变量的地址 2指针的算术运算指针的算术运算符:+、-、++、--两个指针相加或相减都是不允许的。main(){intx=1,y=5,*pi;chara[10],*pc;pi=&x;//变量的地址放入指针中pi--;pc=a;//将数组的首地址赋给指针gets(a);printf(“%d,%c”,*pi,*(++pc));}指针减1,即指针指向下一个数据,指针所指地址移动多少依数据的类型而定。输入:abcd输出:5,b指向字符串的第1个元素 3指针的比较通常,两个或多个指针同时指向一个目标时,才能使用指针比较。否则,比较无意义。在队列,栈等数据结构中,经常用到指针比较。4指针运算符与单目运算符的优先级*与++,--优先级相同,但结合性为自右至左。*p++先取出p所指的地址的值,然后指针加1后单目运算符总是在计算完表达式的值然后加1。 *p++*(p++)*(p++)与*(++p)不同,相差一个元素。(*p)++为所p指地址的内容加1,不是p本身加1。main(){inta[4],i,*pi;pi=a;for(i=0;i<4;i++)scanf(“%d,”,pi++);pi=a;for(i=0;i<4;i++)printf(“%d,”,*pi++);}main(){inta[4]={1,3,7,8},i,*p;p=a;printf(“%d,”,*p++);printf(“%d,”,*(p+2));printf(“%d”,(*p)++);}输出结果:1,8,3 5C语言的内存动态分配函数malloc()函数作用:内存分配函数,在程序运行期间动态分配空间头文件:stdlib.handmalloc.h用法:void*malloc(size_tsize);size—需要分配的字节数。 返回值:成功时,返回新分配块的首地址,如果没有足够的空间,返回NULL。分配完成后,块内容不变,如分配零个字节,则返回NULL。说明:返回的指针指向空类型,在使用时要强制转换为相应类型。注意:由于堆不是无限大,不能保证每次申请分配内存都能成功,故每次使用新分配的地址时,首先要检查首地址是否为空。使用空地址肯定会导致程序的崩溃。 free函数()作用:释放已分配的内存块用法:voidfree(void*block);头文件:stdlib.h,malloc.h说明:释放由调用函数或其它内存分配函所分配的内存块。使用前,指向内存块首地址的指针必须强制转换为指向空类型的指针。 例8.1:栈程序的例。栈是一种常用的数据结构,在编译程序、解释程序、电子报表等与系统有关的软件中经常使用。栈的特点是数据先进后出,用栈来存放数据,就象平常堆盘子,最后放上去的盘子最先拿走。为了理解程序,在此先对栈作一介绍。 栈的结构栈是线性结构,有栈底和栈顶,用二个指针指向栈底和栈顶,栈中数据指针是浮动的。整数栈示意图当数据指针等于栈顶指针时,栈空,小于栈顶指针,则栈下溢。当数据指针等于栈底指针,栈满,大于栈底指针,栈上溢。(一般情况下,栈底的地址比栈顶高)栈中数据233089…1530120-45栈顶栈底数据指针ptosbos 栈的操作:栈的操作只有两种,将数据放入栈中和从栈中取出数据,分别称为进栈和出栈,或压入(push)或弹出(pop)。栈中的数据,要放入时,最能放在最上面,取数据时,也最能从最上面取出。栈是不允许从栈的中间取数据的。//数据出栈函数pop(){if(p==tos){printf("underflow");}p--;return*p;}//数据压栈函数voidpush(i){if(p>bos){printf("stackfull");return;}*p=i;p++;} //exam81.cvoidpush(int);int*p,*tos,*bos;main(){intvalue;p=(int*)malloc(50*sizeof(int));if(!p){printf("allocationfailure! ");return;}tos=p;bos=p+50;do{scanf("%d",&value);if(value!=0)push(value);elseprintf("thisisit%d ",pop());}while(value!=-1);} voidpush(inti){if(p==bos){printf("stackoverflow");exit(1);}*p=i;p++;}pop(){if(p==tos){printf("stackunderflow ");exit(1);}p--;return*p;} 例8.2利用栈来编写一个简单的表达式计算程序为了使用程序简单,表达式采用后缀表示式。表达式的中缀表示法3+4表达式的后缀表示法(3,4)+现有中缀表达式:3*(2+4)-6相应的后缀表达式:((3,(2,4)+)*,6)- 输入3,2,4,并进栈324输入运算符+,弹出两个数据运算2+4运算结果6压入栈中6输入运算符*,弹出两个数据运算3*6运算结果18压入栈中18输入6,并压入栈中6输入运算符-,弹出两个数据运算18-6结果12压入栈中12栈的运行过程栈顶栈底利用栈计算后缀表达式过程:后缀表达式((3,(2,4)+)*,6)- //exam82.c#include#include#include#defineMAX100int*p;int*tos;int*bos;voidpush(int);main(){inta,b;chars[8];p=(int*)malloc(400);if(!p){printf("allocationfailure ");exit(1);}tos=p;bos=p+MAX-1;printf("fourfunctioncalculator ");do{printf(":");gets(s);switch(*s){case'+':a=pop();b=pop();printf("%d ",a+b);push(a+b);break;case'-':a=pop();b=pop();printf("%d ",b-a);push(b-a);break;case'*':a=pop();b=pop();printf("%d ",b*a);push(b*a);break;case'/':a=pop();b=pop();if(a==0){printf("divideby0 ");break;}printf("%d ",b/a);push(b/a);break;default:push(atoi(s));}}while(*s!='q');free(tos);}计算后缀表达式的程序 voidpush(i){//数据入栈if(p>bos){printf("stackfull ");return;}*p=i;p++;}pop(){//数据出栈if(p==tos){printf("stackunderflow ");}p--;return*p;} bos栈顶栈底栈空p=tos返回ptosbos栈底栈顶栈下溢pbos 5指针作为参数的作用在C语言中,变量作为参数传给函数,当参数在函数内部被修改时,结果不能传回调用它的过程。但用指针作为参数时,在函数中修改的结果可以传回。main(){inta;a=10;f(a);printf(“a=%d”,a);}f(inti){i=100;}输出结果:a=10main(){inta;a=10;f(&a);printf(“a=%d”,a);}f(int*i){*i=100;}输出结果:a=100 指针为什么能将在函数中修改的参数值传回?这是因为指针实际上就是地址。将指针作为参数传给函数,就是将指针所指地址传给了函数,函数将指针所指地址的内容修改后,在主函中得到的还是原来的变量地址,但地址中内容被修改了,主函数也就得到了修改后的内容。在上例中,左边的程序不能将函数中修改的参数值传回,而右边的函却可以。这是因为左边程序用的参数是变量,右边用的参数是地址,即指针。 §8.3指针与数组指针与数组的关系十分密切,但使用也比较复杂,使用中要特别注意。一、利用指针访问数组元素数组名就是数组的首地址,也就是指针。其实,在用数组下标访问数组时,隐含使用指针访问。例inta[10];在访问数组的元素时,如访问第0个元素,用数组名,访问第2个元素,用(a+2)来访问,访问数组的第i个元素,用(a+i)访问。这也是C中为什么数组元素从零开始的原因。(a+i)是指针,因为a是指针。但a是不能改变的指针,所以象a++这样的操作是不能执行的。 在某些情况下,用指针访问数组比用下标更快些。#includemain(){inti,a[10],*p;p=a;for(i=0,i<10;i++)scanf(“%d,”,&a[i]);for(i=0;i<10;i++)printf(“%d,”,*p++);}将数组的首地址赋给指针用指针访问数组时,即要定义数组,还要定义类型相同的指针。用下标访问时,每次要计算(a+i)的值,用指针访问时,只需计算p++。单目加法比赋值加法快得多。 但用指针来访问数组,比下标访问更容易出错。#includemain(){inti,a[10],*p;p=a;for(i=0,i<10;i++)scanf(“%d,”,p++);for(i=0;i<10;i++)printf(“%d,”,*p++);}此例与上例相同,只是将输入改为指针,但结果出错。执行完输入后,指针已指向数组的最后。当用指针将数组输出时,指针所指内容不是数组内容。p=a;增加一条语句,使指针重新指向数组的首地址为什么?怎样修改程序使之能正确运行? 例8.3编写一函数,由实参传来一个字符串,统计此字符串中的字母、数字、空格和其它字符的个数,在主函数中输入字符串以及输出上述结果。分析:对于此类问题,一般用数组和指针来处理比较方便。算法:由于问题比较简单,只要顺序从字符串中取出字符,将进行相应的判断,并将结果存入变量中。数据结构:要存放字符串,需要一个字符数组,存入字母等的多少,可用一个整型数组,对字符数组进行操作,可用字符指针。 //exam83.c#include#includeintsum(char*,int*);main(){chars[100];inta[4]={0,0,0,0};gets(s);sum(s,a);printf("%d,%d,%d,%d",a[0],a[1],a[2],a[3]);}sum(char*s,int*a){do{if(isalpha(*s))(*a)++;elseif(isdigit(*s))(*(a+1))++;elseif(isspace(*s))(*(a+2))++;else(*(a+3))++;}while(*s++);}取出指针(a+3)所指地址内容,并将该内容加1 例8.4写一函数,输入一行字符,将此字符串中最长的单词输出。前面我们已用字符数组编写过该程序。现在用字符数组和指针来编写,注意比较两者的差别。仍用两种方式来编写程序。方式1:用两个字符数组来处理。数组word保存目前找到的最长的单词,数组wordtemp存放每次取出的单词。如果取出的单词比word里的单词长,将用它替换word中已有的单词,即将它拷贝到word中。当字符串查找完毕,word中保存的是最长的单词。 //将一行字符中的最长的单词输出。//exam84.c#include#include#includevoidsum(char*,char*);main(){chars[100],word[20]={' '};gets(s);sum(s,word);puts(word);} voidsum(char*s,char*word){charwordtemp[20]={' '};char*p,*temp;p=word;temp=wordtemp;do{if(isalpha(*s))*temp++=*s;else{*temp=‘ ’;//便于数组能用串函数处理if(strlen(wordtemp)>strlen(word))strcpy(word,wordtemp);temp=wordtemp;//取下一单词从头开始}}while(*s++);} 用第二种方式编写程序//将一行字符中的最长的单词输出。//exam85.c#include#includemain(){chars[100];intlen=0,posi=0,i;gets(s);sum(s,&len,&posi);for(i=posi;i*len){*len=templen;*posi=temposi;}templen=0;temposi=i+1;}i++;}while(*s++);} 二、指向数组的指针前面介绍的指针都是指向某一个类型的数据,有时候使用不是很方便。在C语言中引进了指向数组的指针,简称数组指针。定义方式:Typedata(*symbol)[num][num1]…说明:symbol为指针标识符,typedata为数组元素的类型,num为数组元素的个数。 //数组指针使用的例//exam86.c#includemain(){intbx[10]={1,2,3,4},i;charname[4][9]={{“吴一”},{“张三”},{"李四"},{"王五"}};char(*p)[9];//定义数组指针,指针向有9个元素的一维字符数组p=name;//将二维数组的首地址赋给p,建立p与name联系for(i=0;i<4;i++)printf("bx=%d,name=%s ",bx[i],(p+i));}上面的程序中,数组指针p指向有9个元素的一维字符数组,它正好是二维数组的一行元素。用p可以很方便地操作二维数组的每行。当p加1时,指针指向二维数组的下一行。对于二维数组,name[i]*(p+i)对于一维数组,a[i]*(p+i) //exam87.c#includemain(){intt,i,j;char*pt;pt=(char*)malloc(100*80);text(pt);}text(char*pt){char(*p)[80];inti,t=0;p=pt;do{printf("%d:",t++);gets(p);}while(**p++);p=pt;for(i=1;i<=t;i++){printf("%d:",i);puts(p++);}}文本编辑程序p是指向数组的指针,*p是指向一维数组首地址的指针,故**p是一维数组第0个元素的内容。所以,p是指针的指针。空行结束输入 //exam88.c#include#includemain(){intbx[10]={7,8,1,2,10,9,3,4,5,6},i;charname[10][9]={{“钱七”},{“周八”},{“吴一”},{“郑二”},{“洪十”},{“陈九”},{"张三"},{"李四"},{"王五"},{"赵六"}};sort(bx,10,name);search(bx,6,10,name);for(i=0;i<10;i++)printf(" bx=%d,name=%s",bx[i],name[i]);} //利用数组指针排序的函数sort(intbx[],intn,charname[]){inti,j,x,k;charc[9],(*p)[9];p=name;for(i=1;i=0&&bx[j]>x){bx[j+1]=bx[j];strcpy((p+j+1),(p+j));j--;}bx[j+1]=x;strcpy((p+j+1),c);}}两函数基本一样,只是处理每行姓名字符串用的方法不一样,显然,用数组指针更方便。//不用数组指针的排序函数sort(intbx[],intn,charname[]){inti,j,x,k;charc[9];for(i=1;i=0&&bx[j]>x){bx[j+1]=bx[j];strcpy(name[j+1],name[j]);j--;}bx[j+1]=x;strcpy(name[j+1],c);}} search(intbx[],intbxs,intmax,charname[]){intmin=0,mid,k;mid=(max+min)/2;while(1){if(bxs==bx[mid])break;if(bxs>bx[mid])min=mid;elseif(bxsmain(){inti;for(i=0;i<4;i++)serror(i);putchar(‘ ’);}serror(intnum){char*error[]={“cann’topenfile ”,“readerror ”,“writeerror ”,“mediafailure ”};printf(“%s”,error[num]);}指针数组,每个指针指向相应字符串的首地址error也是指针的指针,**error为字母c 将字符串“Ilovechina!”的首地址赋给指针,不是将字符串本身赋给指针。四、字符指针与字符串在C语言中,字符数组常常用指针来操作。对于字符串,一般都是严格的顺序存取方式,故指针是明显的选择。例:main(){char*p;p=“Ilovechina!”;puts(p);}输出Ilovechina!例:main(){chara[14];a=“Ilovechina!”;puts(a);}编译出错。在定义数组时系统给数组分配了地址,在定义字符串时系统同样给字符串分配了另一个地址。 //exam811.c#includechar*is_in(char,char*);main(){charc,*p;inta;c='o';p="peopleol";p=is_in(c,p);if(p){a=*p;printf("a:%c ",a);}p=is_in(c,p);if(p)printf("*p=%c ",*p);}char*is_in(charch,char*s){while(*s)if(*s++==ch)returns;return0;}输出结果:a=p*p=l变量ch中的字符如果包含在串s中,返回指向该字符下一个字符的指针,如果没发现相匹配的字符,返回空指针。 §8.4函数指针函数指针所指的数据不是一般的类型,而是函数。虽然函数不是变量,但函数在存储器内占有实际的地址,这个地址可以用来赋给指针。赋给指针的地址是函数的入口地址。所谓入口地址,是程序在编译后,系统分配给函数的开始地址。利用函数指针,可用其调用函数,也可用来使函数作为参数传递给其它函数。定义:datatype(*symbol)();int(*f)()f是一个指向函数的指针,该函数返回整型值 //exam810.c#include#include#includedoublequad_poly(double);main(){doublex;constdoubledelta=1.0;constdoublefirst=0.0;constdoublelast=10.0;double(*fx[3])();inti;charch;fx[0]=quad_poly;fx[1]=sqrt;fx[2]=log;for(i=0;i<3;i++){x=first;while(x<=last){printf("f(%lf)=%lf ",x,fx[i](x));x+=delta;}printf("pressanykeytocontinue");ch=getch();}}doublequad_poly(doublex){doublea=1.0,b=-3.0,c=5.0;return((x*a)*x+b)*x+c;}fx是一个数组,该数组的元素是指针,指针指向函数,函数的返回值是双精度。故fx是一个指向返回值为双精度的函数的指针数组。 §8.5指针的指针指针的指针,是指指针指向的内容是另一个指针的地址。地址值地址地址值指针变量指针指针变量…36920008050…2012变量i变量j变量k变量的地址200020042008指针p2201220162020指针的指针p1内存用户数据区3020p1p2i P1不能直接指向变量i,要先指向另一个指针p2,然后通过指针p2指向变量i,故p1是指针的指针。一般来说,很少有超过指针的指针这种情况。因为指针级数过多,会使程序的跟踪发生困难,且易发生概念错误。定义方式:type**symbol;例:int**p,*x,y;x=&y;p=&x;**p=5;printf(“%d ”,**p);P中保存的是另外一个指针x的地址,故p是指针的指针 §8.6复杂指针的说明在C语言中,指针的说明有时很复杂,这时,要弄清楚指针的含义必须要很小心。在分析时,以被说明对象为中心,比较对象两边运算符的优先级,在只剩下一边或只有一边有运算符的情况下依次完成运算符的说明。优先级相同时,则依结合性决定。 例:char*(*p[])();P是一个数组,因为数组运算符的优先级比指针运算符高该数组的每个元素为指针指针指向函数函数返回一个指针返回的指针指向字符型数据p是一个指向返回字符指针的函数的指针数组。 int(*(*(*p())[])())[];P是一个函数该函数返回一个指向数组的指针该数组为指向函数的指针数组该函数返回一个指向整型数组的指针 存取命令行参数所谓存取命令行参数,就是程序名后所带的参数。比如在格式化磁盘时的命令formata:,其中format是命令,a:是参数。命令行参数的一般格式如下程序名参数1参数2参数n 在C语言中,实现方式为:main(intargc,char*argv[]){}1argc返回命令行参数的数目,其数目包含程序名2argv是存取这些参数的一个字符指针数组。其中: