计算机编程数据类型、运算符与表达式教案_第1页
计算机编程数据类型、运算符与表达式教案_第2页
计算机编程数据类型、运算符与表达式教案_第3页
计算机编程数据类型、运算符与表达式教案_第4页
计算机编程数据类型、运算符与表达式教案_第5页
已阅读5页,还剩74页未读 继续免费阅读

下载本文档

版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领

文档简介

第三章数据类型、运算符与表达式每种高级语言都根据其所应用的范围规定了它所可以使用的数据类型。一般有以下三种不同范畴的数据类型:(1)基本类型:不可再分的最基本的数据项。这种类型包括整型、实型、双精度型、复型、逻辑型及字符型等;(2)构造类型:由已知的简单类型通过一定的构造方法构造出来的新类型。这种类型包括数组、记录、集合等;(3)指引元类型:即指针,用于构造各种形式的动态或递归数据结构,如链表、树、栈、图等。C语言作为一种用途广泛的通用程序设计语言,提供了以上各个范畴的数据类型。掌握和运用数据类型的概念,就要着重掌握以下几点:(1)各种数据类型的定义方式;(2)各种数据类型的存储结构和取值范围:(3)各种数据类型所允许执行的运算;(4)不同数据类型之间混合运算的规则。一.五种基本数据类型数据类型关键字字符型char整型int浮点(单精度)型float双精度型double无值类型voidC语言中数据有常量与变量之分,它们分别属于以上这些类型。二.常量与变量.常量和符号常量:在程序运行过程中,其值不能被改变的量称为常量。例如:10、-2、3.14、,a,、’d,等。C语言还提供了符号常量(或符号常数),即用一个符号(标识符)来代表一个常量。定义的方法有两种:宏定义和const限定。(1)宏定义的一般形式为:#define标识符字符串(参见第九章)例如:#definePI3.14159265(2)const限定的形式为:const 数据类型 符号常数=常数;例如:constdoublePI=3.14159265注:符号常量名一般习惯用大写,P39例题3-1。.变量:在程序运行过程中,其值可以被改变的量称为变量。用来标识变量名、函数名、数组名、类型名、文件名的有效字符序列称为标识符。C语言规定标识符只能由字母、数字和下划线三种字符组成,且第一字符必须为字母或下划线。例如:,li_ming,A12等。但要注意:大写字母和小写字母被认为是两个不同的字符。标识符长度没有统一的规定,TurboC则允许32个字符。在C语言中,要求对所有用到的变量作强制定义,也就是“先定义,后使用”。三.整型数据.整型常数的表示方法:十进制整常数:由正负号后跟一个数字串组成,正号可以不写,且开头的数字不能是0。例如:123,-456,0等八进制整常数:以数字0打头,后跟0〜7组成的数字串。例如:0123。如果是负数,则在打头的数字。前面冠以负号,如:-037,-0489等。十六进制整常数:以数字0和大写字母X或小写字母x打头,后跟0〜9及A〜F(a〜f)组成的数字字母串。其中,A〜F(a〜f)分别表示十进制的10〜15。例如:0x2f,-0xbA3F等。注:C语言能自动进行十进制、八进制和十六进制整数之间的转换。.整型变量a)整型数据在内存中的存放形式:有关补码的问题P41。b)整型变量的分类:P42有符号基本整型 [signed]int无符号基本整型 unsignedint有符号短整型 [signed]short[int]无符号短整型 unsignedshort[int]有符号长整型 [signed]long[int]无符号长整型 unsignedlong[int]ANSI标准定义的整数类型的取值范围:P43表3.1c)整型变量的定义对变量的定义,一般是放在一个函数的开头部分的声明部分(也可以放在函数中某一分程序内,但作用域只限它所在的分程序)。例3-2d)整型数据的溢出:例3-3.整型常量的类型一个整型常量后面可以加上后缀u(U)、1(L)或ul(UL),则分别构成无符号数、长整型数或无符号长整型数。P45四.实型数据.实数又称浮点数的表示方法a)十进制小数形式:由数字和小数点组成。例如:123.454。b)指数形式:如123e3或123E3都代表123X1()3。字母e之前必须有数字,且e后面的指数必须为整数。注意应用“规范化的指数形式”来表示。.实型变量a)实型数据在内存中的存放形式:通常在微机系统中,一个实型数据在内存中占4个字节(32位)。一般用24位表示小数部分(包括符号),以8位表示指数部分(包括指数的符号)。b)实型变量的分类:单精度(float)、双精度(double型)和长双精度型(longdouble)»P46。c)实型数据的舍入误差:例3-4.实型常量的类型许多C编译系统将实型常量作为双精度型来处理(占64位),以保证计算结果更精确,但是运算速度降低了。所以,可以在数的后面加字母f或F(如1.56f,654.87F),这样编译系统就会按单精度(32位处理)了。五.字符型数据1.字符常量C的字符常量是用单引号括起来的一个字符。如:3,%,,厅等。此外还允许用一种特殊形式的字符形式的字符常量,就是以一个“\”开头的字符序列。见P48表3-3。请注意:*\0'或'\000'是代表ASCH码为0的控制字符,即“空操作”,它将用在字符串中。2.字符变量字符型变量用来存放字符常量,且只能放一个字符。并规定以一个字节来存放一个字符。3.字符数据在内存中的存储形式及其使用方法将一个字符常量放到一个字符变量中,实际上并不是把该字符本身放到内存单元中去,而是将该字符的相应的ASCII码放到存储单元中。其形式与整数的存储形式类似,所以,字符型数据和整型数据之间的转换就比较方便了。例如:3-6,3-7o因此,C语言允许字符数据与整数直接进行算术运算。4.字符串常量字符串常量是用一对双引号括起来的字符序列。注意区分字符常量和字符串常量。每-个字符串的结尾系统会自动加一个字符串结束标志“\0”。六.变量赋初值C语言允许在定义变量的同时对它赋值。例如:inta=3;floatf=3.45;charc='a';若是对相同数据类型的变量赋值,则可以写成:inta=3,b=4,d=5;或inta,b=2;七.各类数值型数据间的混合运算不同类型的数据要先转换成同一类型,然后进行运算。转换规则是:P54图3.10。总之,原则就是“就高不就低”。例P55教材。八.算术运算符和算术表达式.C的运算符有以下几类:P56共13种类型。.算术运算符和算术表达式a)基本的算术运算符:+、一、*、/、%注:两个整数相除的结果为整数。b)算术表达式和运算符的优先级与结合性在表达式求值时,先按运算符的优先级别高低次序执行。结合方向(结合性),算术运算符的结合方向为“自左至右具体见P375附录HI说明。c)强制类型转换运算符:形式是(类型名)(表达式)例如:(int)x+y,(double)a注:强制类型转换时,得到一个所需类型的中间变量,原来变量的类型未发生变化。例3-8.d)自增、自减运算符++i,一—i在使用i之前,先使i的值加(减)1。i++,i一一在使用i之后,先使i的值加(减)1。e)有关表达式使用中的问题说明:P58-59,九.赋值运算符和赋值表达式.赋值运算符:“=",将一个数据赋给一个变量。.类型转换float(double)->int 舍弃实数的小数部分。int—>float(double) 数值不变,以浮点数形式存储到变量中。double->float截取其前面7位有效数字,但应注意数值范围不能溢出。char—>int参见P60。int>short,long—>char只将其低8位原封不动地送到char型变量(即截断)。0signedint—>long 要进行符号扩展,将整型数的16位送到long型低16位中,如果int型数据为正值,则10ng型变量的高16位补0;如果int型变量为负值,则long型变量的高16位补1,以保持数值不改变。g)unsignedint—>longint只需将高位补0即可。h)将非unsigned型数据赋给长度相同的unsigned型变量,也是原样照赋,见例3-9。不同类型的整型数据间的赋什川I根到底就是:按存储单元中的存储形式直接传送。.复合的赋值运算符:在赋值符“=”之前加上其他运算符,可以构成复合的运算符。例如:a+=3等价于a=a+3,x%=3等价于x=x%3。C语言规定可以使用的10种复合运算符为:+=、-=、*=、/=、%=、VV=、>>=、&=、入=、|=.赋值表达式形式:<变量><赋值运算符〉〈表达式〉,按照“自右至左”的结合顺序。例如:a=b=c=5 (赋值表达式值为5,a、b、c的值均为5)a=5+(c=6) (表达式值为11,a值为11,c值为6)a=(b=4)+(c=6) (表达式值为10,a值为10,b等于4,c等于6)十.逗号运算符和逗号表达式形式:表达式1,表达式2, 表达式n 表达式的值是表达式n的值。例如:a=3*5,a*4 其结果是60。因为逗号运算符级别最低!(a=3*5,a*4),a+5,其结果是20。见P64。第四章 顺序程序设计一.c语句概述C语句可以分为以下5类:.控制语句,完成一定的控制功能。If()~else〜(条件语句)For() ~ (循环语句)While()~ (循环语句)Do〜while()(循环语句)Continue (结束本次循环语句)Break (中止执行switch循环语句)Switch (多分支选择语句)Goto (转向语句)Return (从函数返回语句)说明:括号()表示其中是•个条件,〜表示内嵌的语句。例如:if(x>y)z=x;或if(x>y)z=x;elsez=y;.函数调用语句。由一次函数调用加一个分号构成一个语句。例如:printf(lThisisaCstatemenf,);.表达式语句。由一个表达式构成一个语句,最典型的是由赋值表达式构成一个赋值语句。如:a=5是一个表达式,但a=5;则是一个赋值语句。主要是看最后是否有分号。任何表达式都可以加上分号而成为语句,表达式能构成语句是C语言的一个重要特色。.空语句。即只有一个分号的语句,它什么也不做。.可以用{}把一些语句括起来成为复合语句,也称分程序。例如:{t=x;x=y;y=t;)注意:复合语句中最后一个语句中最后的分号不能省略不写!.赋值语句赋值语句是由赋值表达式加上一个分号构成。C语言的赋值语句具有其他高级语言的赋值语句的一切特点和功能。也有区别:.c语言中的赋值号“=”是一个运算符,在其他大多数语言中赋值号不是运算符。.关于赋值表达式与赋值语句的概念,其他多数高级语言没有“赋值表达式”这一概念。因为赋值表达式可以包括在其他表达式之中,例如:if((a=b)>0)t=a;.数据输入输出的概念.所谓输入输出是以计算机主机为主体而言的。.C语言本身不提供输入输出语句,输入和输出操作是由函数来实现的。这printf和scanf不是C语言提供的“输入输出语句”,而只是函数名而己。因为C编译系统与C函数库是分别进行设计的,因此不同的计算机系统所提供函数的数量、名字和功能是不完全相同的。不过,有些通用的函数(如printf和scanf等),各种计算机系统都提供,成为各种计算机系统的标准函数。C语言函数库中有一批“标准输入输出函数”,它是以标准的输入输出设备(一般为终端设备)为输入输出对象的,如:putchar(输出字符),getchar(输入字符),printf(格式输出),scanf(格式输入),puts(输出字符串),即ts(输入字符串)。.在使用C语言库函数时,要用预编译命令"include”将有关的“头文件”包括到用户源文件中。格式:include<文件名.h>或include”文件名.h”。C语言库函数见教材P381附录。.字符数据的输入输出.单字符输出函数putchar格式:putchar(c)其中c可以是字符型变量或整型变量。功能:向输出设备输出一个字符。例4-1.也可以输出控制字符。如:putcharC'n'),输出一个换行符。P71.单字符输入函数getcha格式:getchar()它没有参数。功能:从终端(或系统默认的输入设备)输入一个字符。例4-2.注意,在运行时,输入完字符'a'应按回车键;同时getchar()只能接收一个字符;该函数得到的字符可以赋给一个字符变量或整型变量,也可以不赋给任何变量,作为表达式的一部分。.单字符输入函数getche()和getch()使用getchar()函数时,输入字符后必须按回车键以后输入的字符才能接受。这种方式有时会和环境很不协调,于是,人们更多地使用getch()和getche()函数。这两种函数的最大特点是:功能上与getchar()函数类似,但输入数据时不必按回车键,只要键入需要的字符就可以了。getche()函数的使用与getchar()完全相同,但它包含在标题文件conio.h中。可以将上述例题中的语句调换一下,程序运行的结果不变。getch()的作用与getche()基本一致,只是不把读入的字符回显到屏幕上,即输入的字符不在屏幕上显示,这对输入密码或菜单选择非常有用。将上述例题中的语句修改一下进行验证。getch()函数也包含在标题文件conio.h中。注:getche()和getch()函数都不是由ANSI标准定义的,但大多数C编译系统都提供这两种函数。.格式输入与输出.格式输出函数printf格式:printf(格式控制,输出列表)或printf(参数1,参数2,……,参数n)功能:向输出设备输出若干任意类型的数据。说明:1)“格式控制”是用双引号括起来的字符串,也称“转换控制字符串”,它包括两种信息:格式说明:由“%”和格式字符组成,如%d,%f等。它的作用是将输出的数据转换为指定的格式输出。格式说明总是以“%”字符开始的。普通字符:即需要原样输出的字符。如:逗号、空格、换行符等。2)“输出列表”是需要输出的一些数据,可以是表达式。例如:printf("%d%d”,4b);或printf("a=%db=%d”,a,b);3)格式字符,对不同类型的数据用不同的格式字符,常用的有以下几种格式字符:d,i:以带符号的十进制形式输出整数(正数不输出符号,按实际位数)o:以八进制无符号形式输出整数(正数不输出前导符0,按实际位数)x,X:以十六进制无符号形式输出整数(正数不输出前导符0,按实际位数),输出的字母a〜f大小写由x(或X)的形式决定。u:以无符号十进制形式输出整数c:以字符形式输出,只输出单个字符S:输出字符串f:以小数形式输出浮点数(单、双精度数),隐含输出6位小数。e,E:以指数形式[-Jm.dddddde±nn输出浮点数(单、双精度数)。g,G:选用%f或%e格式中输出宽度较短的一种格式,不输出无意义的0o%%:输出一个%符号字母1:用于长整型整数,可加在格式符d、。、x、u前面m(代表一个整数):数据最小宽度n(代表一个整数):对实数,表示输出n位小数;对字符串,表示截取的字符个数。-:输出的数字或字符在域内向左靠齐说明:令格式转换说明符的个数应与输出项的个数相等,且顺序和类型应对应一致。令除X、E、G外,其他格式字符必须用小写字母,如%d不能写成%D令可以在printf函数中的“格式控制”字符串内包含第三章P48“转义字符”表中的字符。2.格式输入函数scanf1)格式:scanf(格式控制,地址列表)说明:“格式控制”的含义同printf函数;“地址列表”是由若干个地址组成的列表,其中可以是变量的地址,或字符串的首地址。例如4-9。注意:输入数据时,在两个数据之间以一个或多个空格间隔,也可以用回车键、跳格键tab,但不能用逗号作分隔符(指定的除外)。2)格式说明:d,i:用来输入有符号的十进制整数u:用来输入无符号的十进制整数o:用来输入无符号八进制整数x,X:用来输入无符号的十六进制整数(大小写作用相同)c:用来输入单个字符s:用来输入字符串,将字符串送到一个字符数组中,在输入时以非空白字符开始,以第一个空白字符结束。字符串以串结束标志'\n'作为其最后一个字符。f:用来输入实数,可以用小数形式指数形式输入。e,E,g,G:与f作用相同,e与f,g可以互相替换。字母1:用于输入长整型数据(可用%ld,%lo,%lx)以及double型数据(用%If或%le)h:用于输入数据所占宽度(列数),域宽应为正整数。域宽:指定输入数据所占宽度(列数),域宽应为正整数。*:表示本输入项在读入后不赋给相应的变量说明:令对unsigned型变量所需的数据,可以用%u,%d或%o,%x格式输入令可以指定输入数据所占列数,系统自动按它截取所需的数据。如:scanf(“%3d%3d”,&a,&b);或scanf("%3c”,&ch);令如果在%后有一个“*”附加说明符,表示跳过它指定的列数。如:scanf(**%2d%*3d%2d”,&a,&b):令输入数据时不能规定精度。如:scanf(“%7.2「,&a);是不合法的。3)使用scanf函数时应注意的问题:scanf函数中的“格式控制”后面应当是变量地址,而不是变量名。如:scan("%d,%d'',a,b):是不对的。如果在“格式控制”字符串除了格式说明以外还有其他字符,则在输入数据时应输入与这些字符相同的字符,否则,当scannf函数在输入的数据中找不到这些字符时就自选终止。如:scanf("%d,%d”,&&&b);不能用scanf函数去显示一个提示信息。如:scanf(4iEnteranintegertox:%d”,&x);反而增加了输入的难度。在用“%c”格式输入字符时,空格字符和“转义字符”都作为有效字符输入。如:scanf("%c%c%c”,&cl,&c2,&c3);在输入数据时,遇以下情况就认为数据结束:令遇空格,或按“回车”或“跳格键(Tab)”令按指定的宽度结束,如“%3d”,只取3歹h六.顺序程序设计举例例4-10、4-11,4-12第五章选择结构程序设计一.关系运算符和关系表达式.C语言提供的6种关系运算符:TOC\o"1-5"\h\z<小于 、<= 小于或等于>优先级相同(高)> 大于>= 大于或等于 j== 等于 ]卜优先级相同(低)!= 不等于说明:关系运算符的优先级低于算术运算,而高于赋值运算。.关系表达式用关系运算符将两个表达式连接起来的式子,称为关系表达式。关系表达式的值是一个逻辑值,即“真”或“假”,但C语言没有逻辑型数据,而是以数字1代表“真”,以数字0代表“假”。注意:关系运算符的结合方向是自左向右。二.逻辑运算符和逻辑表达式用逻辑运算符连接起来的式子就是逻辑表达式。如:(a>b)AND(b>c).逻辑运算符及其优先次序C语言提供的三种逻辑运算符:&& 逻辑与(相当于其他语言中的AND)II 逻辑或(相当于其他语言中的OR)! 逻辑非(相当于其他语言中的NOT)逻辑运算的“真值表”见P89表5-1。逻辑运算符的优先次序是:!(非)一&&(与)-||(或)注意:"!”高于算术运算符;“&&”和“II”低于关系运算符。.逻辑表达式C语言编译系统在给出逻辑运算结果时,以数值1代表“真”,以0代表“假”,但在判断一个量是否为真时,以0代表“假”,以非0代表“真”。即将一个非零的数值认作为“真:如:5>3&&8<4-!0最终的结果为“0”。实际上,逻辑运算符两侧的运算对象不但可以是。和1,或者是0和非。的整数,也可以是任何类型的数据。如字符型、实型或指针型等。系统最终以0和非0来判定它们属于"真"或"假如:'c'&&d的值为1.在逻辑表达式的求解中,并不是所有的逻辑运算符都被执行,只是在必须执行下一个逻辑运算符才能求出表达式的解时,才执行该运算符。即可以优化以提高效率。如:a&&b&&c和a11b11c»三.if语句if语句的三种形式:if(表达式)语句如:if(x>y)printfC%d”,x);if(表达式)语句1else语句2如:if(x>y)printf("%d”,x);elseprintf(44%dM,y);if(表达式1)语句1elseif(表达式2)语句2elseif(表达式3)语句3elseif俵达式m)语句melse语句n说明:A(表达式)的类型不限于逻辑表达式,可以是任意的数值类型(包括整型、实型、字符型、指针型数据)。如:if(3)printf('*OK");第二、三种形式的if语句中,在每个else前面有一个分号,整个语句结束处有一个分号。在if和else后面可以只含一个内嵌的操作语句,也可以有多个操作语句,此时用花括号u{}"将几个语句括起来成为一个复合语句。如:if(a+b>c&&b+c>a&&c+a>b){s=0.5*(a+b+c);area=sqrt(s*(s-a)*(s-b)*(s-c));printf("area=%6.2f”,area);}elseprintf("itisnotatrilateral");例5-1、5-2.2.if语句的嵌套形式:if()if()语句1else语句2elseif()语句3else语句4应当注意if与else的配对关系。else总是与它上面的最近的if语句配对。如果if与else的数目不一样,为实现程序设计者目的,可以加花括号来确定配对关系。例5-3。3.条件运算符若if语句中,在表达式为“真”和“假”时,且都只执行一个赋值语句给同一个变量赋值时,可以用简单的条件运算符来处理。如:

if(a>b)max=a;if(a>b)max=a;max=(a>b)?a:b;elsemax=b;r条件运算符要求有3个操作对象,称三目(元)运算符,它是C语言中唯一的一个三目运算符。其形式一般为:表达式1?表达式2:表达式3说明:>条件运算符的优先级别比关系运算符和算术运算符都低,但优先于赋值运算符。>条件运算符的结合方向为“自右向左”。如:a>b?a:c>d?c:d>条件表达式不能取代一般的if语句,只在if语句中内嵌的语句为赋值语句(且两个分支都给同一个变量赋值)时才能代替if语句。>条件表达式中,表达式1的类型可以与表达式2和表达式3的类型不同。如:x?,a?b',表达式2和表达式3的类型也可以不同,此时条件表达式的值的类型为二者中较高的类型。如:x>y?1:1.5结果为实型值1.5或l.Oo例5-4。四.Switch语句C语言提供switch语句(开关语句)直接处理多分支选择,它相当于PASCAL语言中的CASE语句。其一般形式为:switch(表达式){case常量表达式1:语句1case常量表达式2:语句2case常量表达式n:语句ndefault:语句n+1)说明:>switch后面括号内的“表达式”,ANSI标准允许它为任何类型。>当表达式的值与某case后面的常量表达式的值相等时,就执行case后面的语句,否则就执行default后面的语句。>每一个case的常量表达式的值必须互不相同,否则就会出现互相矛盾的现象。>各个case和default的出现次序不影响执行结果。因为所有case的表达式都将判断,满足相等条件的语句都会被执行.如果想执行完一个case分支后,使流程跳出switch结构,即终止switch语句的执行,可以用一个break语句来达到此目的。见P99例子。>switch与if语句的不同之处在于switch语句只能测试相等条件,而if语句结构既可测试相等条件,也可测试其他条件。因此,switch语句不能完全if语句。>多个case可以共用一组执行语句。如P100五.程序举例例5-5、5-6、5-7.第六章循环控制一.概述在许多问题中需要用到循环控制。循环语句用来描述具有规律性的重复运算,可以大大缩短程序的长度,使程序简单明了。重复计算或操作在计算机中占有特殊的地位,这是因为:1.许多实际问题本身就是重复。例如,若干个数累加或连乘;打印表格时等。2.计算机最擅长进行简单的重复计算,因此,计算机算法的一个基本特点就是把一个复杂的计算过程归结为简单过程的多次重复。例如,绝大多数非线性方程和微分方程都要归结为用相同的迭代公式进行多次迭代来求解。正由于重复计算或操作的重要性和广泛性,各种程序设计语言都设置了循环语句来描述这类问题。C语言提供了三种形式的循环语句。二.goto语句以及用goto语句构成循环goto语句为无条件转向语句,它的一般形式为:goto 语句标号;说明:语句标号用标识符表示,它的定名规则与变量名相同,即由字母、数字和下划线组成,其第一个字符必须为字母或下划线。但结构化程序设计方法主张限制goto语句,因为滥用该语句将会使程序流程无规律、可读性差。若需要从多层循环的内层循环跳到最外层循环时才用到goto语句。只是这种用法不符合结构化程序设计的原则,一般不宜采用,除非万不得已时才使用。见例6-1。三.While语句while语句用来实现“当型”循环结构。一般形式如下:while(表达式)语句功能:当表达式为非0值时,执行while语句中的内嵌语句。其特点是:先判断表达式,后执行语句。例6-2注意:循环体如果包含一个以上的语句,应该用花括号括起来,以复合语句形式出现;在循环体中应有使循环趋向于结束的语句。四.do-while语句do-while语句的特点是先执行循环体,然后判断循环条件是否成立。一般形式为:do循环体语句while(表达式);注意比较while语句和do-while语句的不同点。例6-3、6-4«五.For语句for语句的形式:for(表达式1;表达式2;表达式3)语句或for(循环变量赋初值;循环条件;循环变量增值)语句它的执行过程如下:①先求解表达式L②求解表达式2,若其值为真(值为非0),则执行for语句中指定的内嵌语句,然后执行下面第3步;若为假(值为0),则结束循环,转到第5步。③求解表达式3。④转回上面第2步继续执行。⑤循环结束,执行for语句下面的一个语句。例:for(i=l;i<10;i++)sum=sum+i;若将for语句改成while语句的话,其形式如下:表达式1;while(表达式2)(语句表达式3;)说明:.for语句的一般形式中的"表达式1”可以省略,如:for(;I<10;i++)sum=sum+i,此时应在for语句之前给循环变量赋初值。注意省略表达式1时,其后的分号不能省略。.如果表达式2省略,即不判断循环条件,循环将无终止地进行下去(即出现死循环)。如:for(i=l;;i++)sum=sum+i;这时只能使用break语句强行终止。.表达式3也可以省略,但此时程序设计者应另外保证循环能正常结束。如:for(i=l;1<=100;){sum=sum+i;i++;}.可以省略表达式1和表达式3,只有表达式2,即只给循环条件。如:for(;1<=100;){sum=sum+i;i++;}.3个表达式都可以省略,如:for(;;)语句。这时程序不判断循环条件(认为表达式2为真值),循环变量不增值。无终止地执行循环体(即死循环)。.表达式1可以是设置循环变量初值的赋值表达式,也可以是与循环变量无关的其他表达式。如:for(sum=0;i<=100;i++)sum=sum+i;同样,表达式3也可以是与循环控制无关的任意表达式。表达式1和表达式3可以是一个简单的表达式,也可以是逗号表达式(中间用逗号间隔)。如:for(sum=0,i=l;i++)sum=sum+i:或for(i=0j=100;i<=j;i++j—)k+=i*j;注:在逗号表达式内按自左至右顺序求解,整个逗号表达式的值为其中最右边的表达式的值。.表达式一般是关系表达式或逻辑表达式,但也可以是数值表达式或字符表达式,只要其值为非零,就执行循环体。如:for(i=0;(c=getchar())!='\n';i+=c);虽然for语句的功能很强,也很灵活,但过分地利用这一特点会使for语句显得杂乱,可读性降低,建议不要把与循环控制无关的内容放到for语句中。六.循环的嵌套一个循环体内又包含另一个完整的循环结构,称为循环的嵌套。上述介绍的三种循环都可以互相嵌套。但要注意,必须遵守完全包含的原则。见教材PU3图。七.几种循环的比较参见教材P114说明。while循环、do・while循环和for循环,可以用break语句跳出循环,用continue语句结束本次循环。而对用goto语句和if语句构成的循环,不能用这两个语句进行控制。八.Break语句和continue语句break语句用break语句既可以使流程跳出switch结构,继续执行语句下面的一个语句,也可以用来从循环体内跳出循环体。其形式为:break;注意:break语句不能用于循环语句和switch语句之外的其他语句中。continue语句continue语句的形式为:continue;该语句的作用为结束本次循环,即跳过循环体中下面尚未执行的语句,接着进行下一次是否执行的判定。注意与break语句的区别。见P115教材图示。例6-5。九.程序举例例6-6、6-7、6-8、6-9、6-10程序设计思维方法前面已经详细介绍了C语言的各种程序控制结构,于是,我们就有了编制简单程序所必需的工具。进行程序设计需要很强的逻辑思维能力,是一种极富创造性的智力劳动。对初学者来说而言,往往最使他们望而生畏的,也正是这一点。其实,电脑作为人脑的模拟,在许多方面与人的思维习惯是一致的。如果能迅速地把人们的思维习惯与程序设计的思维方法吻合起来,无疑会较快地提高程序设计能力。下面介绍程序设计的三条基本思维原则:枚举原则,归纳原则和抽象原则。.枚举原则枚举法,也称穷举法,是人们常用的一种思维方法。有一些问题,我们无法用一个计算公式来求得它们的解,它们的解可能离散地分布在某个有限或无限的集合里。枚举法,就是逐一列举这个集合里的各个元素,并加以判断,直至求得所需要的解。枚举法,思维简单,但处理起来比较繁杂,因为要列举的集合元素很多,列举、判断的过程极耗费精力,这就限制了它的实用价值。但是,计算机的运算速度极快,可以不知疲倦地进行单调而浩繁的工作,因此,枚举法在程序设计中得到了广泛的应用。尤其是在排列、组合、数据分类、信息检索、多解方程的求解以及人工智能程序设计中,大量使用了枚举法。使用枚举法,主要掌握两条原则:1)确定搜索范围当然,这个范围必须是有限的而不是无限的。例如,求素数,就是一个无限范围内的问题,用计算机也只能找到某一个最大素数,因为它受到计算机容量和计算时间的限制。在确定搜索范围时,有的问题比较明显,有的问题就不那么明显,就要进行分析后再确定。2)选择搜索策略如何枚举,按照一条什么样的路线来逐一枚举,就是搜索策略问题。上述两条原则使用得好坏,对程序的工作量有巨大的影响。例:用100元钱买100只鸡,每只公鸡5元,每只母鸡3元,每只小鸡1元,要求每种鸡至少买一只,且必须是整只的,问各种鸡各买多少只?显然,这是一个组合问题,也可以看成是一个三元一次方程问题。只能用枚举法。令i,j,k分别表示公鸡、母鸡和小鸡的只数。为了确定i,j,k的取值范围,可以有不同的方法。我们可以看到,不同的方法,其计算量可能相差甚远。方法一:令i,j,k的搜索范围分别为:i:1〜20(公鸡最多能买20只)j:1〜33(母鸡最多能买33只)k:1〜100(小鸡最多能买100只)于是可以用三重循环来逐个搜索:程序如下#include<stdio.h>main(){inti,j,k;for(i=l;i<=20;i++){for(j=l;j<=33;j++){for(k=1;k<=100;k++){if(i+j+k!=100)continue;if(i*5+j*3+k/3.0!=100)continue;printf(Hi=%dj=%d,k=%d\n,,,ij,k);)在这个程序中,循环体被执行了20X33X100=66XI000次,且每次循环要作两次判断。方法二:令i,j,k的搜索范围分别为(保证每种鸡至少买一只)i:1〜18(公鸡最多能买18只)j:1〜31(母鸡最多能买31只)k:lOO-i-j(当公鸡和母鸡数量确定后,小鸡的数量即随之而定)用二重循环来实现,程序如下:#include<stdio.h>main(){inti,j,k;for(i=l;i<=18;i++){for(j=l;j<=31;j++){k=100-i-j;if(i*5+j*3+k/3.0!=100)continue;printf(',i=%d,j=%d,k=%d\n,\i,j,k);在这个程序中,循环体被执行了18X31=558次,其计算量不足方法一的1%。方法三:由题意可得到下列方程组:ri+j+k=100I5i+3j+k/3=100由此可得:14i+8j=200由于i和j至少为1,实际上i最大为13,j最大为23,这样可以得到如下程序:#include<stdio.h>main(){inti,j,k;for(i=l;i<=13;i++){for(j=l;j<=23;j++){k=100-i-j;if(i*5+j*3+k/3.0!=100)continue;printf(',i=%dj=%d,k=%d\nUj,k);)))在这个程序中,循环体被执行了13X23=229次,略大于方法二的一半。方法四:由方法三中的方程7i+4j=100可得:j=(100-7i)/4因此,可以用单层循环编制程序如下:#include<stdio.h>main(){inti,j,k;for(i=l;i<=13;i++){j=25-7*i/4;k=100-i-j;if(i*5+j*3+k/3.O!=100)continue;printf(,,i=%dj=%d,k=%d\n,\i,j,k);在这个程序中,循环体只被执行了13次,工作量不足方法三的5%。下面再举一个例子说明选择不同的搜索策略对工作量的影响。例:求整数a和b的最小公倍数i。如果i是a和b的最小公倍数,则i必能被a和b整除,同时,i必须是自然数,所以其取值范围为1~8。方法一:从1开始,依次增加1,直到第一个能被a和b整除为止,这个i就是a和b的最小公倍数。于是可编制程序如下:#include<stdio.h>main(){inta,b,i;scanf(n%d%d';&a,&b);i=0;while(1){i++;if(i%a==0)if(i%b==O){printf(H%d\nH,i);break;)}}假设a=7,b=5,则循环体将被执行次。方法二:令i从a开始,而不是从1开始,使i每次增加a而不是增加1,这就保证了i总是a的倍数。因此,每次只要判断i能否被b整除就可以了。一旦判断成立,i就是a和b的最小公倍数。程序如下:#include<stdio.h>main(){inta,b,i;scanf(n%d%dn,&a,&b);i=0;while(1){i+=a;if(i%b=O){printf(H%d\n,',i);break;)}在这个程序中,对同样的a、b,循下一环次数仅为方法一的1/7。由此可见,选择合适的搜索策略,对减少计算量具有重要的意义。.归纳原则归纳法,是从大量的特殊性中总结出规律性或一般性的结论。在数学中,归纳法主要用于证明;而在程序设计中,则大量使用归纳法,以便找到规律性的东西。一旦找到了规律,用计算机解决就是轻而易举的事了。我们非常熟悉的一个问题是对数列或级数求和,通过找到它们的通项公式就可以方便地求和。归纳法,在程序设计上主要表现为递归和迭代。这是计算机算法的一个基本特点。我们常常通过递归和迭代的方式把一个复杂的计算过程化为简单过程的多次重复,这种重复很容易用循环来实现。例如,有一张足够大的纸,厚0。09毫米,问将它对折多少次后可以达到珠穆朗玛峰的高度(8848米)?用传统的数学方法可以计算出:n=log28848-log2(9*10-5)用计算机方法,只需将初值a=9*IO。每次乘以2(表示对折后的高度),然后判断是否已超过8848。若已超过,则记下乘2的次数就是对折的次数。程序如下:#include<stdil.h>main(){intn=0;floata=0.09e-3;while(1){n++;a=a*2;if(a>8848)break;)printfC4m=%d\n,,,n);}这样就把一个复杂的对数问题转化为简单的加法和乘法的多次重复。本程序的循环体总共被执行27次,故n=27,表明对折27次后,纸的高度已超过8848米。掌握递归与递推的思维方法和程序设计方法,在程序设计中是极为有用的。许多用传统的数学方法无法求解的问题(例如,求高次方程的根、求积分、求微分方程的解等),它们大多数都不能用传统的数学方法求解,只能用计算机求数值解。用计算机求数值解,基本上都离不开递推方法。而递推方法,正是归纳法。归纳法的数学模型具有如下统一的形式:g(x)(n=0)Fn(x)=«H(Fmi(x))(n>0)倒如:①阶乘n!TOC\o"1-5"\h\z.1 n=0n!=v1n(n-l)!n>0②菲波那数列E(X)r 0 n=0Fn(x)=v 1 n=lI Fn.2(x)+Fn.i(x) n>l③累加和④连乘积它们都有一个初始值和一个迭代公式(也称递推公式)。因此,使用归纳法需要分两步进行:•确定初值•确定递推公式,并反复使用这个递推公式,直到求出问题的答案为止。累加和、连乘积、阶乘、多项式求值、求极值、排序等都要用到归纳法。例如:求n!。用p来存放阶乘值,显然其初值为1,递推公式为:p=p*i,程序如下。#include<stdiLh>main(){inti,n;floatp=1;scanf("%d”,&n);for(i=l;i<=n;i++)p=p*i;primf("%d!=%10f\nM,n,p);)例如:求任给n个数中的最大值和最小值。假设任给n个数为ai,a2,…,an,用max和min分别存放最大值和最小值。先令max=a],min=ai,漏将max和min依次与a1,a2,…,an比较,若发现maxvai,贝ij令max二出;若min〉4,则令min=a"全部比较完后,max和min就是否中的最大值和最小值。程序如下。#include<stdio.h>main(){inti,n;floata,max,min;scanf("%d”,&a);max=min=a;for(i=2;i<=n;i++){scanf(4<%f\&a);if(max<a)max=a;if(min>a)min=a;)printf(4tmax=%f,min=%f\max,min);}.抽象原则抽象原则是程序设计的另一个基本原则。为了解决一个复杂的问题,人的智力往往不可能一下子就触及到问题的细节方面。在分析了问题的要求之后,我们问题首先设计出一个抽象算法。这一算法往往要借用有关学科中的概念与对象,而不去考虑问题的细节方面,诸如数组怎样加下标,怎样访问内存,怎样存储数据等,只是在抽象数据上实施一系列抽象操作。这些数据和操作反映了问题的本质属性,而将所有的细节都抽象掉了。因此,这样的算法描述非常容易得到有关学科中相关理论的证明。下一步,再将算法求精,使之更加细化、清晰。这时,算法中就包括了更多的细节,这些细节已再不是问题所在学科中的细节,而是怎样求解的细节。例如,可能考虑那些与求解该问题有关的数值方法方面的细节,如此下去,这一求精过程可能还得连续进行几个较低的级别,直至使用某种数据结构,能轻而易举地用某种计算机语言编制程序为止。如果我们能掌握大量的程序设计基本单元,就可能使求精过程大为缩短,程序设计能力也就提高了。下面,我们通过一个实例说明怎样在复杂问题的程序设计中进行抽象和逐步求精。这个实例就是用计算机验证哥德巴赫猜想。哥德巴赫猜想是说一个大偶数总可以分解成两个素数之和。这一猜想目前还未被完全证明。但我们可以用计算机来验证它。即对一个大偶数找到两个素数,若它们的和等于这个大偶数,那么猜想对这个大偶数就得到了验证。这种验证可以进行若干次,但并不能使猜想得到证明。反过来,若对一个大偶数找不到对应的两个素数,则可以反证猜想不成立。下面就考虑如何验证它。第一步,先根据哥德巴赫猜想本身的概念,对一个大偶数i,总可以写成i=m+n的形式。这是一个不定方程,可能存在多组解,也可能没有解。用前面介绍的枚举法,可以找出这些解,或者找不到解。如果找到一对特定的m和n,它们既是素数,其和又等于i,那么对i来说,哥德巴赫猜想就得到了验证。这种思维方法是抽象的,不涉及到具体的i、m和n,而是以数学原则为基础的,容易验证。于是我们可以画出算法框图。第二步,细化一点,考虑如何确定素数m。我们知道,对一个大偶数而言,其所在范围内将有有限多个素数,例如,i=30,其中的素数有2,3,5,7,11,13,17,23,29共9个。我们总可以对一个大偶数范围内所有素数逐一枚举出来。另外,只有偶数4可以唯一地写成4=2+2的形式,超过4的任何偶数都不可能分解成一个素数2,只能分解成两个奇数。从而可以得到一个确定m的算法,即m只能取i内的所有奇数。如果确定了素数m,由n=i-m就可唯一地得到n,剩下的工作就是验证n是不是素数。如果n不是素数,就要重新确定m。第三步,考虑如何验证m和n是素数。这是两个相同的过程。如果我们对验证素数的程序设计单元非常熟悉,就不必再进一步求精。否则,再对验证素数进行求精。根据素数的性质:对一个整数k,若它不能被k的平方根内的所有素数整除,则k必为素数。因此,就可以设计算法了。经过以上工作,就可以较为容易地写出如下程序:#include<stdio.h>#include<math.h>main(){inti,m,n,j;for(i=6;i<=1000;i+=2){m=l;while(1){m+=2;if(m>=i){printf("哥德巴赫猜想不成立!\n");gotoal;}else{for(j=2;j<=sqrt((float)m);j++){if(m%j==0)gotoa2;}gotoa3;)a2:continue;a3:n=i-m;for(j=2;j<sqrt((float)n);j++){if(n%j==0)goto14;}printf(**%d=%d+%d\n,,,i,m,n);break;a4:;))al:;)第七章数组一.数组的定义和引用一维数组的定义:类型说明符数组名[常量表达式]例如:inta[10]:说明:1)数组名定名规则和变量名相同,遵循标识符定名规则。2)数组名后是用方括弧括起来的常量表达式,不能用圆括弧。例如:inta(10):3)常量表达式表示元素的个数,即数组长度。但数组的下标从0开始,所以使用下标时应注意不要越界。4)常量表达式中可以包括常量和符号常量,不能包含变量。一维数组元素的引用数组必须先定义,然后使用。C语言规定只能逐个引用数组元素而不能一次引用整个数组。数组元素的表示形式为:数组名[下标]其中“下标”既可以是整型常量,也可以是整型表达式。例如:a[0]=a[5]+a[7]-a[2*3].例7-1。一维数组的初始化对数组元素的初始化可以用以下方法实现:1)在定义数组时对数组元素赋值。例如:inta[10]={0』,2,3,4,5,6,7,8,9};2)可以只给部分元素赋值。例如:inta[10]={0,l,2,3,4);这表示只给前面5个元素赋初值,后5个元素值为0。3)如果想使一个数组全部中全部元素值为0,可以写成:inta[10]={0,0,0,0,0,0,000,0,};但不能写成inta[10]={0*10};4)在对全部数组元素赋初值时,可以不指定数组长度。例如:inta[3]={123,4,5};可以写成inta[]={1,2,3,4,5};因为花括弧中有5个数,系统就会据此自动定义a数组的长度为5o但如果定义的数组长度与提供初值的个数不相同,则数组长度不能省略。一维数组程序举例:例7-2、7-3。二.二维数组的定义和引用.二维数组的定义,一般形式为:类型说明符数组名[常量表达式][常量表达式】例如:floata[3][4],b[5][10];定义a为3行4列的数组,b为5行10列的数组。注意不能写成floata[3,4],b[5,10];C语言对二维数组采用这样的定义方式,使我们可以把二维数组看作是一种特殊的一维数组:它的元素又是一个一维数组。这种处理方法在数组初始化和用指针表示时显得很方便。而且,C语言中,二维数组中元素排列的顺序是按行存放的,即在内存中先顺序存放第一行的元素,再存放第二行的元素。同理,对多维数组的定义和使用也是和二维数组一样。.二维数组的引用二维数组的元素表示形式为:数组名[下标][下标],其中“下标”既可以是整型常量,也可以是整型表达式。如:a[2-l](2*2-1],但不能写成a[2-l,2*l]形式。同时,也应注意不能使数组的下标越界。另外,也应该严格区分在定义数组时用的a[3][4]和引用数组元素a[3][4]时的区别。.二维数组的初始化可以用下面的方法对二维数组初始化:1)分行给二维数组赋初值。如:inta[3][4]={{l,2,3,4},{5,6,7,8],{9,10,ll,12}};2)可以将所有数据写在一个花括弧内,按数组排列的顺序对各元素赋初值。例如:inta[3][4]={1,2,3,4,5,6,7,8,9,10,11,12};3)可以对部分元素赋初值。如:inta[3][4]={{l},{6},{9}};它的作用是只对各行第■列的元素赋初值,其余元素值自动为0。4)如果对全部元素都赋初值,则定义数组时对第一维的长度可以不指定,但第二维的长度不能省。如:inta[3][4]={1,2,3,4,5,6,7,8,9,10,11,12};也可以写成下面的形式inta[][4]={1,2,3,4,5,6,7,8,9,10,11,12);.二维数组程序举例例747-5.三.字符数组.字符数组的定义,同前面的定义方法类似。如:charc[10];.字符数组的初始化charc[10]={T,'*'a','m',',,'h','a','p','p','y'};把10个字符分别赋给c[0]到c[9]10个元素。如果花括弧中提供的初值个数(即字符个数)大于数组长度,则按语法错误处理;如果初值个数小于数组长度,遇只将这些字符赋给数组中前面那些元素,其余的元素自动定为空字符(即'\0');若个数与预定的数组长度相同,在定义时可以省略数组长度,系统会自动根据初值个数确定数组长度。charcU={T1,H「m7Th;a:p;p,,y};3.字符数组的引用例7-6、7-7«.字符串和字符串结束标志在C语言中,将字符串作为字符数组来存放。系统对字符串常量自动加一个‘'0'作为结束符,所以,在程序中往往依靠检测'\0’的位置来判定字符串是否结束,而不是根据数组的长度来决定字符串长度。那么,我们也可以用以下的方法对字符数组进行初始化:charc[]={l*Iamhappy");或charc[]="Iamhappy”;说明:字符数组并不要求它的最后一个字符为‘\0',甚至可以不包含‘\0'。.字符数组的输入输出字符数组的输入输出可以有两种方法〉1)逐个字符输入输出。用格式符“%c”输入或输出一个字符。例7-6。2)将整个字符串一次输入或输出。用格式符“%s”输出。注意:•输出字符不包括结束符'\0'。用“%s”格式符输出字符串时,printf函数中的输出项是字符数组名,而不是数组元素名。如果数组长度大于字符串实际长度,也只输出到遇‘\0'结束。如果一个字符数组中包含一个以上'\0',则遇第一'\0'时输出就结束。需要注意:scanf函数中的输入项是字符数组名。输入项为字符数组名时,不要再加地址符&,因为在C语言中数组名代表该数组的起始地址地址。如:由于C语言用•维字符数组存放字符串,允许用数组名进行输入或输出•个字符串,因此,也可以把一维字符数组看作相当于其他语言中的“字符串变量”。.字符串处理函数puts(字符数组)功能:将一个字符串(以'\0'结束的字符序列)输出到终端。例如:puts(str);其中字符串str可以包含转义字符。如:charstr[]={**China\nBeijing,,);和scanf函数相比不同的是,输入字符串中的空格也会被正常接收。gets(字符数组)功能:从终端输入一个字符串到字符数组,并且得到一个函数值。该函数值是字符数组的起始地址。例如:gets(str);一般利用gets函数的目的是向字符数组输入一个字符串,而不大关心其函数值。strcat(字符数组1,字符数组2)功能:连接两个字符数组中的字符串,把字符串2接到字符串1的后面,结果放在字符数组1中。函数调用后得到一个函数值一-字符数组1的地址。例如:charstrl[30]={''People'sRepublicof41);charstr2[]={"China”}:printf("%s”,strcat(strl,str2));说明:字符数组1必须足够大,以便容纳连接后的新的字符串;连接后新的字符串最后保留一个‘\0’。shvpy(字符数组1,字符串2)功能:将字符串2复制到字符数组1中去。例如:charstrl[10]={"China”};strcpy(strl,str2);说明:字符数组1必须定义得足够大,以便容纳被复制的字符串。其长度不应小于字符串2的长度。字符数组1必须写成数组名形式(如strl),字符串2可以是字符数组1中,也可以是一个字符串常量。如:strcpy(strl,“China");复制时连同字符串后面的‘\0’一起复制到字符数组1中。不能用赋值语句将一个字符串常量或字符数组直接给一个字符数组。如:str1={“China”};str2=strl;都是不合法的。只能用strcpy函数处理。可以用stmcpy函数将字符串2中前面若干个字符复制到字符数组1中去。如:strncpy(strl,str2,2);stivmp(字符串1,字符串2)功能:比较字符串1和字符串2o比较的结果由函数值带回:a)如果字符串1=字符串2,函数值为0:b)如果字符串1>字符串2,函数值为一正整数;c)如果字符串1<字符串2,函数值为一负整数:注意:对字符串比较,不能用以下形式:if(strl==st⑵printf("yes”);只能用if(strcmp(strl,str2)==0)print:"yes");strlen(字符数组)功能:测试字符串长度。函数的值为字符串中字符的实际长度,不包括'\0'在内。strlwr(字符串)功能:将字符串中大写字母换成小写字母。strupr(字符串)功能:将字符串中小写字母换成大写字母。.字符数组应用例7-8、7-9.第八章函数一.概述C语言中的函数相当于PASCAL语言中的过程或FORTRAN语言中的程序段(或其它高级语言中的子程序)。而子程序的作用是由函数完成的。C程序通常由一个或多个函数组成,每个函数完成一定的功能,函数之间通过调用关系完成总体功能。如上所述,C程序是函数的集合体。一个C程序由一个或多个函数组成;每个函数都具有相对的独立性和单一的功能。在这些组成C程序的若干函数中,必须有一个,而且只能有一个函数是主函数,它被命名为main,它可以任意多次调用其他函数,而其他函数又可以互相调用;当main()调用其他函数时,我们称main()为主调用函数,而其他函数称为被调用函数。当其他函数调用另外一些函数时,它又成为调用函数。这些函数,可以集中或分散存放在一个或几个C程序文件之中,程序文件均以.C为扩展名。见例8-1。应该注意的是,C程序的函数是一种独立性很强的程序模块,所有的程序模块都处于平等地位,不存在从属关系,即在程序文件运行时,任何函数都既可以调用其他函数,又可以被其他函数调用,甚至还可以自我调用。教材有关函数的说明P144二.函数定义的一般形式在C语言中,程序模块的功能是由函数来实现的。从用户角度看,C函数可分为标准库函数和用户定义函数两类,前者是系统定义的,分别存放在不同的标题文件中,用户只要用#include包含其所在的标题文件即可直接使用它们;后者则是用户为解决自己的特定问题自行定义的函数。.无参函数的定义形式类型标识符函数名(){声明部分语句)说明:无参函数一般不需要带回函数值,因此可以不写类型标识符。如例8-1。.有参函数的定义形式类型标识符函数名(形式参数列表){声明部分语句)例如:intmax(intx,inty){intz;z=x>y?x:y;retum(z);)说明:上述例子中return后面的括号中的值(z)作为函数的返回值。如果在定义函数时不指定函数类型,系统会隐含指定函数类型为int型。.可以有“空函数”类型标识符函数名(){)例如:dummy(){}说明:调用此函数时,什么工作也不做,没有任何实际作用。在主调函数中写上该函数,表明“这里要调用一个函数”,而现在暂时没有编写。.对形参的声明的传统方式在老版本C语言中,对形参类型的声明是放在函数定义的第2行,也就是不在第一行的括号内指定形参的类型,而在括号外单独指定。如:intmax(x,y)intx,y;{intz;z=x>y?x:y;retum(z);)上述两种用法是等价的。ANSI新标准推荐“现代”的写法。三.函数参数和函数的值1.形式参数和实际参数在定义函数时函数名后面括号内的变量名称为“形式参数”(形参);在主调函数中调用一个函数时,函数名后面括号内的参数(或表达式)称为‘'实际参数"(实参)。例8-2。关于形参与实参的说明:在定义函数中指定的形参,在未出现函数调用时,它们并不占内存中的存储单元,只有在发生函数调用时,函数中的形参才被分配内存单元。调用结束后,形参所占的内存单元也被释放。实参可以是常量、变量或表达式,只是要求它们一定要有确定的值。在被定义的函数中,必须指定形参的类型。实参与形参的类型应相同或赋值兼容,C语言规定,实参变量对形参变量的数据传递是“值传递”,即单向传递,只由实参传给形参,而不能由形参传回给实参。.函数的返回值希望通过函数调用使主调函数能得到一个确定的值,这就是函数的返回值。对函数返回值有关的说明如下:1)函数的返回值是通过函数中的return语句获得的。如果需要从被调用函数带回一个函数值(供主调函数使用),那么被调用函数中必须包含etum语句。反之,则不必。因为一个函数中可以有一个以上的return语句,执行到哪一个return语句,哪一个语句就起作用。2)函数值的类型。C语言规定,凡不加类型说明的函数,一律自动按整型处理。应该注意的是,在定义时对函数值说明的类型一3)如果函数值的类型和代turn语句中表达式的值不一致,则以函数类型为准。例8-34)如果被调用函数中没有return语句,并不带回一个确定的、用户所希望得到的函数值。实际上,函数并不是不带回值,带回的只是一个不确定的、没有用的值。5)为了明确表示“不带回值”,可以用“void”定义"无类型"(或空类型)。这样系统就保证不使函数带回任何值。所以,凡不要求带回函数值的函数,一般应定义为void类型。四.函数的调用1.函数调用的一般形式:函数名(实参表列);说明:如果是调用无参函数,则''实参表列”可以没有,但括号不能省略。实参与形参的个数应相等,类型应一致。实参与形参按顺序对应,一一传递。如果实参表列包括多个实参,对实参求值的顺序并不是确定的,有的系统按自左至右顺序求实参的值,有的系统按自右至左顺序求实参的值。TurboC和MSC的按自右而左的顺序求值。见例子8-4。.函数调用的方式按函数在程序中出现的位置来分,可以有以下三种函数调用方式:1)函数语句把函数调用作为一个语句,使它能完成一定的操作,而不要求它带回值。2)函数表达式函数出现在一个表达式中,这里要求它带回一个确定的值以参加表达式的运算。例如:c=3*max(a,b)+100;3)函数参数函数的调用作为,一个函数的实参(嵌套)。例如:m=max(a,max(b,c));.对被调用函数的声明和函数原型在一个函数中调用另一函数需要具备的条件:1)被调用的函数必须是已经存在的函数2)如果使用库函数,一般还应该在本文件开头用#include命令将调用有关库函数时所需用到的信息“包含”到本文件中来。3)如果使用用户定义的函数,而且该函数与调用它的函数(即主调用函数)在同一个文件中,一般还应该在主调函数中对被调用的函数作声明。例8-5。在函数声明中也可以不写形参,而只写形参的类型。如:floatadd(float,float)-C语言把函数声明称为函数原型。使用函数原型是ANSIC的一个重要特点,其作用主要是利用它在程序的编译阶段对调用函数的合法性进行全面检查。函数原型的一般形式:函数类型 函数名(参数类型1,参数类型2,……)函数类型 函数名(参数类型1,参数名1,参数类型2,参数名2,……)说明:由于编译系统不检查参数名和参数个数,因此参数名是什么都无所谓。但我们不提倡这种用法,因为它末进行全面检查。如果在函数调用之前没有对函数作声明,则编译系统会把第一次遇到的该函数形式作为函数的声明,并将函数的类型默认为int型。最好不要使用这种定义形式。如果被调用函数的定义出现在主调函数之前,可以不必加以声明。P154如果已在所有函数定义之前,在函数的外部已做了函数声明,则在各个主调函数中不必对所调用的函数再作声明。P154五.函数的嵌套C语言的函数定义都是互相平等、独立的,也就是说在定义函数时,一个函数内不能包含另一个函数,即不能嵌套定义函数,但可以嵌套调用函数(在调用一个函数的过程中,又调用另一个函数)。见P155图8-5及例8-6。

六.函数的递归调用在调用一个函数的过程中又出现直接或间接地调用该函数本身,称为函数的递归调用。前者称为直接递归,后者称为间接递归。由于递归非常符合人们的思维习惯,而且许多数学函数及许多算法或数据结构都是递归定义的,因此递归调用具有很大的实用价值。如求n!,菲波那契数列等。见P158图8-9、8-10和例8-7。递归程序分析:递归程序在执行过程中每次调用自身,由于其哑元是以堆栈方式存储的,因此,每次调用时共值互不相同,且不会相互干扰,这是理解递归程序的基本依据。递归程序的执行过程可分为两个阶段,即回推和递推。Fac(5)=5*fac(4)以求n!为例,见例8-8o所谓“回推”,是指在求n!时,必先求出(n-1)!,而(n-1)!还未求出来,就要回推到求(n-2)!,…,直到回推到求1!,这时,1!已知,就不再回推了。然后进入递推阶段,所谓“递推”,实际上是回推的相反过程,即根据返回值计算公式由1!求出2!,由2!求出3!,…,直至求出n!。Fac(5)=5*fac(4)Fac(5)=l20回推过程递推过程回推过程递推过程用递归方法求n!程序:floatfac(intn)(floatf;if(n<0){printf(“n<0”,dataerror!");f=—1;}elseif(n==0)Iln==l)f=l;elsef=fac(n—l)*n;return(f);}main()intn;floaty;printf(4tinputaintegernumber:");scanf("%d”,&n);y=fac(n);printf(4t%d!=%15.0f\n,y);}从上面的执行过程可以看出,在递归回程中,尽管哑元变量名相同,都是n,但是每一次递归,其值(即实元传递来的值)都是不同的。因为哑元是局部变量,是用堆栈方式存动态存储的,每次入栈的值互不干扰,在递推阶段再次依次弹出。再举例子8-9。递归程序设计:> 设计步骤递归过程的设计一般可分为两个步骤1)将问题简化成一个与原问题具有相同特征、但某些参数可能不同的较为简单一些的子问题。如:求n!,先将其化成n*(n—1)!,即先计算(n—1)!。2)进行递归调用,即反复对子问题进行相同的处理。如:(n—1)!=(n—l)*(n—2)!;(n—2)!=(n—2)*(n—3)!>递归过程的终止不应该出现无休止递归的现象,要确定递归终止条件,从而使递归能正常终止。例题:输入一个任意十进制整数,然后按相反的顺序将各位数字打印出来。对一个任意十进制整数,若n/10=0,说明它只有一位,直接将它转换成字符打印出来即可:否则,说明n超过一位,可用n%10取出其最低位,并令n=n/10,从而使其位数减少lo这样,问题就得到简化。因此递归过程可设计为:将指定的数先取出其最低位打印出来,然后用n/10将它减少一位,直至只剩最后一位,即n/10=0为止。程序如下:#include<stdio.h>voidprintd(inti);main(){intn;scanf("%d”,&n):printd(n);)voidprintd(intn){inti;if(n<0){n=n;putchar(“一”);}putchar(n%10+'0');if((i=n/10)!=0)printd(i);)程序中,putcharf-,)语句是将负数的符号打印出来,n%10+XT表示将数字转换为对应的字符,用n%10+48亦可,因为O的ASCII代码值为48。递归程序的优劣:1)递归过程思路清晰,程序简单,符合人们的思维习惯,因此设计起来比较容易,程序的正确性也易于证明。2)有一些问题(如快速排序算法)只能用递归方法实现;还有一些问题,如树、链表等动态数据结构,它们本身就是用递归方法定义的,因此,用递归过程极易实现。3)递归过程中,每次调用都必须首先进行参数替换、现场保护等工作,因而实现效率低;同时,递归过程中存在大量的重复计算,时间和空间开销都比较大。鉴于上述原因,许多程序设计语言都允许使用递归。但实用上,则常常将问题先设计成递归算法,然后再将它变成非递归算法,这样,既使逻辑思维路线清楚,又可提高程序效率。Fib(l)Fib(0)从上图我们可以分析一下,每次递归调用都要计算两次fib函数。其中,Hb(O)计算了三次,fib(l)计算了四次,fib(2)计算了三次,fib(3)计算了二次,fib(4)和fib(5)各计算了一次,总共计算了14次。而如果该问题采用递推算法的话,则程序只需进行四次加法,效率明显提高,时间和开销都比较小。程序如下:fib(intn){intf,fo,fi,i;fo=l;fi=l;for(i=2;i<=5;i++){f=fo+f>;ft=fo;fo=f;}return(f);八.局部变量和全局变量1.局部变量在一个函数内部定义的变量是内部变量,它只在本函数范围内有效,也就是说只有在本函数内才能使用它们,在此函数以外是不能使用这些变量的。这就是“局部变量”。说明:见P1691)主函数main中定义的变量(m,n),也只在主函数中有效,而不因为在主函数中定义就能在整个文件或程序中有效。同理,主函数也不能使用其他函数中定义的变量。2)不同函数中可以使用相同名字的变量,它们代表不同的对象,互不干扰。3)形式参数也是局部变量。4)在一个函数内部,可以在复合语句中定义变量,这些变量只在本复合语句中有效,这种复合语句也称为“分程序”或“程序块”。2.全局变量在函数内部定义的变量是局部变量,而在函数之外定义的变量称为外部变量,外部变量就是全局变量(也称全程变量)。全局变量可以为本文件中其他函数所共用。它的有效范围为从定义变量的位置开始到本源文件结束。见P170例。说明:1)设全局变量的作用是增加了函数间数据联系的渠道。由于函数的调用只能带回一个返回值,因此可以利用全局变量增加与函数联系的方法,从函数中得到一个以上的返回值。2)为了便于区别全局变量和局部变量,在C程序设计人员中有一个不成文的约定,将全局变量名的第一个字母用大写表示。例8-15.3)建议不在必要时不要使用全局变量。因为:全局变量在程序的全部执行过程中都占用存储单元,而不是仅在需要时才开辟存储单元。它使函数的通用性降低了,因为函数在执行时要依赖于其所在的外部变Mo使用全局变量过多,会降低程序的清晰性,人们往往难以清楚地判断出每个瞬时各个外部变量的值。4)如果在同一个源文件中,外部变量与局部变量同名,则在局部变量的作用范围内,外部变量被'‘屏

温馨提示

  • 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
  • 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
  • 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
  • 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
  • 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
  • 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
  • 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。

评论

0/150

提交评论