版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领
文档简介
第7章函数7.1函数的定义7.2函数的调用7.3数据传递方法7.4嵌套调用和递归调用7.5变量的作用域7.6变量的存储类别7.7结构化程序设计方法7.1函数的定义7.1.1函数的结构1.函数头函数头有经典与现代两种形式,现代形式称为函数原型。经典形式: 函数属性函数类型函数名(函数参数表)
参数说明:例如:
static float f1(a,b,c)/*函数特征说明*/ float a,b,c;/*参数说明*/
函数属性为static,函数类型为float,函数名为f1,函数参数表为“a,b,c”。现代形式:函数属性函数类型函数名(参数说明)
例如:
static double f2(floatx,floaty,floatz)
说明:
(1)函数的属性或称函数的存储属性包括内部函数与外部函数两种。内部函数用保留字static描述,又称静态函数。外部函数用保留字extern标识。缺省函数的属性默认为外部函数。外部函数可以供构成程序的所有文件中的函数调用,内部函数只有同一程序文件中的函数才能调用。
(2)函数类型给出函数返回值的数据类型,缺省时默认为int型。当函数没有返回值时,函数为无类型,标准C语言中用保留字void标识无类型(或称空类型)。此时函数相当于其它高级语言中的过程子程序。(3)函数名可以是任何合法的标识符,是程序设计人员为函数取的名字。函数的名字实际上表示的是函数被调用时的入口地址。
(4)函数中的参数称为形式参数(简称形参),参数的主要作用是在函数被调用时实现主调函数与被调函数之间的数据传递,同时参数在相应函数体内可同普通变量一样使用。注意:在现代定义形式中,每个参数必须分别指明其数据类型,即使是相同类型的参数也必须分开说明。(5)函数可以无参数,此时称为无参函数。主函数也可以有参数,这将在第10章中加以介绍。
(6)鼓励读者采用函数原型的现代形式。本书前两部分还是采用传统形式,第三部分采用现代形式。2.函数体函数体由服务于函数的数据说明与执行语句两部分组成。函数的数据说明包括数据定义和数据申明,用以完成数据描述。函数的执行语句完成函数的操作描述。
TurboC中的数据说明必须位于函数体或复合语句的开始处,但在C++中可根据需要随时描述数据。一个函数的函数体可以为空,此时的函数称为空函数。空函数不产生任何操作,常用于程序调试。空函数的形式为:
voidf( ){ }7.1.2返回语句
通过在函数中使用返回语句,返回一个值给函数,同时终止函数的调用,返回主函数。格式:return(表达式);
或return表达式;
功能:(1)计算表达式的值,将表达式的值返回给函数。
(2)从被调用的函数返回主调函数。
说明:
(1)返回值类型应和函数类型一致,不一致时返回值将自动转换成函数类型。
(2)函数中可以有多条返回语句,这时一般与if语句联用,执行到哪一条返回语句,哪一条返回语句就起作用。
(3)函数中无返回语句,则执行至函数体结尾时返回。此时将返回一个不确定的值给函数。
(4)如果只需要从函数中返回,而不需带回值,那么可使用不带表达式的return语句和void类型函数。7.1.3函数的定义(1)需定义函数的属性。(2)需定义函数的类型。(3)给函数取一个名字。(4)设计函数的参数。(5)对函数中使用的量进行定义。(6)对函数的执行部分进行描述。例7–1
求两个数的最大值函数。/*求两个数的最大值函数*/floatmax(x,y)(1)函数值类型floatfloatx,y;(2)函数名max{floatm;(3)函数参数x、y,类型为floatif(x>y)m=x;elsem=y;return(m);/*返回最大值*/}说明:
(1)函数头可以采用现代形式:floatmax(floatx,floaty)(2)函数体也有多种等价形式:形式一:if(x>y)return(x);elsereturn(y);形式二:return(x>y?x:y);例7-2
判断两个数是否是相等函数。方法一:/*判断两个数是否是相等函数*/inteq(x,y)(1)函数值类型int,可以缺省floatx,y;(2)函数名eq{floatt;(3)函数参数x、y,类型为floatif(x==y)t=1;elset=0;return(t);/*相等返回1,不等返回0*/}方法二:
/*判断两个数是否是相等函数*/inteq(x,y)floatx,y;{if(x==y)return(1);elsereturn(0);}方法三:
/*判断两个数是否是相等函数*/inteq(x,y)floatx,y;{return(x==y);}例7-3
符号函数。
方法一:/*符号函数*/charsign(x) /*函数值类型为char*/floatx; /*函数名为sign*/{chars;/*函数参数为x,类型为float*/if(x>=0)s='+';
elses='−';
return(s);
}方法二:
/*符号函数*/ charsign(x) floatx;
{if(x>=0)return('+');
elsereturn('−');}例7-4
平方函数。
/*平方函数*/ floatsq(x) /*函数值类型为float*/ float /*函数名为sq*/ {return(x*x); /*函数参数为x,类型为float*/}例7-5
素数判断函数。/*素数判断函数*/intpn(n) /*函数值类型为int*/intn /*函数名为pn*/{inti; /*函数参数为n,类型为int*/intflag; /*素数标志*/flag=1;
for(i=2;i<=n/2;i++)if(n%i==0){flag=0;break;}return(flag);
}例7-6
阶乘函数。
/*阶乘函数*/ longintfn(n) /*函数值类型为longint*/ intn; /*函数名为fn*/ {inti; /*函数参数为n,类型为int*/ longintt;
t=1;
for(i=1;i<=n;i++)t*=i;
return(t);}例7-7
求三角形面积的函数。/*求三角形面积的函数*/floatsabc(a,b,c)/*函数值类型为float*/floata,b,c;/*函数名为sabc*/{floathl;/*函数参数为a、b、c,类型均为float*/hl=0.5*(a+b+c);
return(sqrt(hl*(hl−a)*(hl−b)*(hl−c)));
}
注意:C语言的函数是平等的并列的关系,C语言的函数体中不能包含函数的定义,即不允许函数嵌套定义。7.2函数的调用7.2.1函数调用的一般形式函数调用的一般形式为:
函数名(实参表);
说明:(1)如果调用无参函数,则无实参表,此时小括号不能省略。
(2)调用时,实参与形参的个数应相同,类型应一致。
(3)实参与形参按顺序对应,一一传递数据。调用后,形参得到实参的值。
(4)实参可以是表达式。如是表达式实参,先计算表达式的值,再将值传递给形参。(5)在C语言中,实参与形参的结合顺序有的系统按自左至右的常规顺序,有的系统则按自右至左的特殊顺序。大多数C(包括TurboC)语言采用自右而左的顺序结合。例如:
inti=3;
printf ("%d,%d",i,++i);①实参与形参自左至右结合,输出3,4。②实参与形参自右至左结合,输出4,4。为了避免出现意外情况,应尽可能将参数表达式的计算移至调用函数前进行。7.2.2函数调用的方式
1.表达式方式
函数调用出现在一个表达式中,这种表达式称为函数表达式。这时要求函数返回一个确定的值以参加表达式的运算。例如:y=max(a,b)+max(c,d),两次函数调用都是表达式的一部分,分别以返回a与b、c与d的最大值参与表达式运算。2.语句方式把函数调用作为一个语句,常用于只要求函数完成一定的操作,不要求函数返回值的情况。这在scanf( )函数及printf( )函数的调用中已多次使用。在其它高级语言中,函数的调用只能以表达式方式进行。C语言中的语句方式调用,实际上还是以表达式方式调用为基础,其实就是一种表达式语句调用。3.参数方式函数调用作为另一个函数的实参。例如:m=max(max(a,b),max(c,d)),max(a,b)与max(c,d)两次函数调用作为另一次max()函数调用的实参,用来求出a、b、c、d的最大值。以参数方式调用实际上是表达式调用方式的一种特殊情况。例7-8
用调用函数的方式来求下面两个三角形的面积。
算法提示:
(1)在主函数中进行能不能构成三角形的判断。
(2)主函数通过两次调用求三角形面积的函数分别求出两个三角形的面积,并输出。
(3)求三角形面积的函数参见例7-7。/*程序7-8,用调用函数的方式来求三角形的面积*/floatsf(a,b,c)/*求三角形面积的函数*/floata,b,c;{floathl;
hl=0.5(a+b+c);
return(sqrt(hl*(hl−a)*(hl−b)*(hl−c)));}main( )/*主函数*/{floatx,y,z;
clrscr( );/*求第一个三角形的面积*/x=3,y=4,z=5;if((x+y>z)&&(y+z>x)&&(z+x>y))printf("三边为%5.2f,%5.2f,%5.2f的三角形面积等于%5.2f\n",sf(x,y,z));
elseprintf("不能构成三角形!\n");
/*求第二个三角形的面积*/x=y=z=1;
if((x+y>z)&&(y+z>x)&&(z+x>y))printf("三边为%5.2f,%5.2f,%5.2f的三角形面积等于%5.2f\n",sf(x,y,z));
elseprintf("不能构成三角形!\n");}运行结果:三边为3.00,4.00,5.00f的三角形面积等于6.00
三边为1.00,1.00,1.00f的三角形面积等于0.43
例7-9
调用函数求2~1000之间的所有素数。算法提示:
(1)对2~1000之间的某个数,调用素数判断函数来判断这个数是否为素数。
(2)对2~1000之间所有数的判断处理,可用for循环999次调用素数判断函数来完成。
(3)素数判断函数参见例7-5。程序如下:/*程序7-9,调用函数求2~1000之间的所有素数*/intpf(n)/*素数判断函数*/intn;{inti;intflag;flag=1;for(i=2;i<=n/2;i++)
if(n%i==0){flag=0;break;}return(flag);}main()/*主函数*/{inti;intcount=0;/*统计素数个数,用以控制输出格式*/clrscr();printf(″2~1000之间的素数如下:\n″);for(i=2;i<=1000;i++)
if(pf(i)==1)
{printf(″%6d″,i);count++;if(count%5==0)printf(″\n″);/*每行输出5个素数*/}}7.2.3被调函数说明
用户自定义函数一般需在调用前在主调函数中进行说明。函数说明是一种申明,是告诉主调函数这里调用的是一个什么样的函数。函数说明的一般形式为:
被调函数类型被调函数名();
注意,不是函数定义,其后有分号。例7-10
求2~n之间的所有素数。main(){intn;inti;intcount=0;intpf();/*函数说明*/clrscr();printf(″请输入n:″);scanf(″%d″,&n);printf(″2~n之间的素数如下:\n″);for(i=2;i<=n;i++)if(pf(i)==1)/*调用前申明的函数*/{printf(″%6d″,i);count++;if(count%5==0)printf(″\n″);}
}/*素数判断函数*/…
有下面一些情况,在调用函数前可不加说明:(1)如果是整型函数或字符型函数,可以不加说明,系统自动按整型函数处理。
(2)如果被调用函数的定义出现在主调函数之前,可以不加说明。例7-8、7-9就属于这种情况。
(3)如果已在所有函数定义之前,或在文件的开头,或在函数的外部已作说明,可以不加说明。除了上述情况,其它情况均必须对被调用的函数在调用前进行说明。
另请注意:(1)函数一般先定义,后使用。
(2)为清晰起见,一般均对被调用的函数在调用前进行说明,以增加程序的可读性。
(3)函数不能重复定义,但可以反复说明。
(4)对于标准函数,还应在文件开头用文件包含指令#include指明调用有关库函数时所需用到的信息。例如:#include″stdio.h″,stdio.h是一个头文件,它包含了输入输出库函数所用到的一些宏定义信息,7.3数据传递方法C语言程序由若干相对独立的函数组成,在程序运行期间,必然存在数据在函数中流动,流入或流出,这就是函数之间的数据传递,也是函数之间的接口。一般语言中,数据传递有参数传递和全局变量传递两种方法,C语言也提供这两种传递方法。参数传递是数据传递的主门(正门),全局变量传递是数据传递的辅门(后门),数据传递时一般宜用参数传递。例如,求三个数的最大值:①主调函数:
m=max(a,b,c);abcmax ↓↓↓↑……m②被调函数:floatmax(x,y,z)xyztfloatx,y,z;
{floatt;
t=x;
if(y>t)t=y;
if(z>t)t=z;
return(t);
}(2)传地址方式将对应实参地址传递给相应形参,实参与形参共享存储单元。这时,一方面可完成批量数据的传递,另一方面形参的改变将引起对应实参的改变,实现数据的双向传递,并将多个数据带回(这在PASCAL语言中用变参实现)。传地址方式增加了函数之间的联系,有副作用,不利于将程序中的错误隔离。7.4嵌套调用和递归调用7.4.1嵌套调用
例7-11
求2~n之间的完数,利用函数的嵌套调用完成。算法提示:
(1)定义一个函数,判断某数是否为完数。
(2)定义另一个函数,求2~n之间的完数,调用完数判断函数完成。程序如下:
main( )/*请读者给出*/ /*求2~n之间的完数的函数,调用完数判断函数完成*/ voidwsh(n) intn;
{inti;
intwf( );/*函数说明*/printf("2~%d之间的完数如下:\n",n);
for(i=2;i<=n;i++) if(wf(i)==1)printf("%6d",i);
}…/*判断x是否为完数的函数*/intwf(x)intx;{inti,s=0;
intw;
for(i=1;i<x;i++)if(x%i==0)s+=i;
if(s==x)w=1;
elsew=0;
return(w);}函数调用过程:主函数→wsh(n)→wf(i)。例7-12
计算的值。
算法提示:
(1)定义求阶乘函数,在此基础上定义求组合数函数。
(2)主函数调用求组合数函数,求组合数函数三次调用求阶乘函数。程序如下:main( )/*请读者给出*/
/*求阶乘函数*/longintjf(n)intn;
{inti;…longintt=1;for(i=1;i<=n;i++)t*=i;return(t);}/*求组合数函数*/longintcmn(m,n)intm,n;
{return(jf(m)/(jf(n)*jf(m−n)); }阶乘计算易产生溢出,宜采用double型进行处理。函数调用过程:主函数→cmn(m,n)→jf(m),jf(n),jf(m−n)。7.4.2递归调用
包含有递归调用的函数称为递归函数。递归算法具有两个基本特征:(1)转化。
(2)终止。例如:计算n!。①转化:n!→(n−1)!→(n−2)!→…→2!→1!→0!。转化的规则是:n!=n*(n−1)!。②终止:n=0,0!=1。相应的递归函数为:
longintjf(n) /*求阶乘的递归函数*/ intn;
{longintj;
if(n>0)j=n*jf(n−1); /*递归调用*/ elsej=1; /*递归终止*/ return(j);}
我们也可先判断是否为递归调用终止条件,如不是,按转化规则转化来写递归函数。longintjf(n)I /*求阶乘的递归函数二*/intn;{longintj;if(n==0)j=1;/*递归终止*/elsej=n*jf(n-1);/*递归调用*/return(j);}如下是计算4!的过程:if(4)=4*jf(3)if(4)=24↓↑
if(3)=3*jf(2)if(3)=6↓↑
if(2)=2*jf(1)if(2)=2↓↑
if(1)=1*jf(0)if(1)=1↓↑
if(0)=1
递归调用分成两个阶段完成。第一阶段是递进阶段(↓所指方向),即递归的调用阶段,完成转化阶段的处理。第二阶段是回归阶段(↑所指方向),也就是递归的返回阶段,即由特定问题的解递推出所求问题的解。从一般意义上讲,这也就是嵌套调用的逐级调用,逐层返回。不过,这里调用的是函数自身,而且一般调用的次数比较多。递归调用的实现要用到堆栈,用堆栈来保留调用现场。递归调用实际上是多重嵌套调用的一种特殊情况。递归调用的次数称为递归的深度。例7-13
计算mn
的递归函数。方法一:①转化:mn→mn−1→mn−2→…→m2→m1→m0。转化的规则是:mn=m*mn−1。②终止:n=0,m0=1。相应的递归函数为:
longintmn(m,n) /*计算m的n次方的递归函数*/ intm,n;
{longintj;
if(n>0)j=m*mn(m,n−1); /*递归调用*/ elsej=1; /*递归终止*/ return(j);
}
方法二:①转化:
n为偶数,mn →mn/2 → … → m0,规则为mn=(mn/2)2;
n为奇数,mn→m(n−1)/2→…→m0,规则为mn=m*(m(n−1)/2)2。②终止:n=0,m0=1。相应的递归函数请读者自己定义。采用方法二,递归调用的次数比方法一要少,效率要高。例7-14
用递归方法计算Fibonacci数列的第n项函数。①转化:n>2,f(n)=f(n-1)+f(n-2)②终止:n=1、2,f(1)=f(2)=1相应的递归函数为:
intfib(n)/*计算Fibonacci数列的第n项*/intn;{intj;if(n>2)j=f(n-1)+f(n-2);/*递归调用*/elsej=1;/*递归终止*/return(j);}
例7-15
年龄问题。有5个人坐在一起,问第5个人多少岁,他说比第4个人大2岁。问第4个人多少岁,他说比第3个人大2岁。问第3个人多少岁,又说比第2个人大2岁。问第2个人多少岁,说比第1个人大2岁。最后问第1个人多少岁,他说是10岁。请问第5个人多少岁?。①转化:n>1,age(n)=age(n-1)+2②终止:n=1,age(1)=10
age(n)/*求年龄的递归函数*/intn;{inta;/*用作存放函数的返回值*/if(n==1)a=10;elsea=age(n-1)+2;return(a);}main()/*主函数*/{printf(″第5个人的年龄=%d\n″,age(5));}运行结果:第5个人的年龄=18
7.5变量的作用域
7.5.1局部变量
(1)在一个函数内部定义的变量是局部变量,只能在函数内部使用。
(2)在主函数内部定义的变量也是局部变量,其它函数也不能使用主函数中的变量。
(3)形式参数是局部变量。
(4)在复合语句中定义的变量是局部于复合语句的变量,只能在复合语句块中使用。
(5)局部变量在函数被调用的过程中占有存储单元。
(6)不同函数中可以使用同名变量。例如,有如下程序:main(){inta=1;intb=2;/*a,b为局部变量*/{intb=1;/*此b非前b,语句块中局部变量*/printf(″复合语句中a=%d,b=%d\n″,a,b);}printf(″复合语句外a=%d,b=%d\n″,a,b);}运行结果:复合语句中a=1,b=1
复合语句外a=1,b=27.5.2全局变量(1)在函数外部定义的变量是全局变量,其作用域是变量定义位置至整个程序文件结束。
(2)使用全局变量,可增加函数间数据联系的渠道。全局变量可以将数据带入在作用域范围内的函数,也可以将数据带回在作用域范围内的其它函数。inta,b;/*a,b为全局变量*/ints(){a=a*a;/*函数中a、b即为前定义的全局变量*/b=b*b;return(a+b);}main(){intsum;a=2;b=3;sum=s();printf(″sum=%d,a=%d,b=%d″,sum,a,b);}运行结果:sum=13,a=4,b=9
当然,这也是一个递推问题,我们可直接用循环来处理。使用递归算法层次分明,形式简练,可读性强,符合人们的思维习惯。对一些能够用递归方法编程的问题,尽量使用递归。但使用递归方法编写的程序执行时的时间、空间开销比较大,这也要求慎用递归。此时可改用迭代的方法或递推的方法,用循环实现。(3)提前引用外部变量,需对外部变量进行说明,或称申明。形式: extern类型变量表;作用:说明这些变量是已在外部定义过的变量。例如,有如下程序:
main(){externinta,b;/*外部变量说明*/
}inta,b;/*外部变量定义*/…
说明:①这样还不如将外部变量提前定义,先定义、后使用。②变量的定义与说明同前面介绍过的函数的定义与说明类似,变量不能重复定义,但可以多次说明。(4)使用程序中非本程序文件的外部变量,也要对使用的外部变量进行同上的申明,或用文件包含处理。
(5)局部变量如与外部变量同名,则在局部变量的作用域内,外部变量存在,但不可见,外部变量的作用被屏蔽。
(6)全局变量在程序运行过程中均占用存储单元。
(7)在编程时,原则上尽量少用全局变量;能用局部变量,不用全局变量,要避免局部变量全局化。7.6变量的存储类别7.6.1变量的存储类别
内存中供用户使用的存储空间分为代码区与数据区两个部分。变量存储在数据区,数据区又可分为静态存储区与动态存储区。静态存储是指在程序运行期间给变量分配固定存储空间的方式。如全局变量存放在静态存储区中,程序运行时分配空间,程序运行完释放。
动态存储是指在程序运行时根据实际需要动态分配存储空间的方式。如形式参数存放在动态存储区中,在函数调用时分配空间,调用完成释放。对于静态存储方式的变量可在编译时初始化,默认初值为0或空字符。对动态存储方式的变量如不赋初值,则它的值是一个不确定的值。在C语言中,具体的存储类别有自动(auto)、寄存器(register)、静态(static)及外部(extern)四种。静态存储类别与外部存储类别变量存放在静态存储区,自动存储类别变量存放在动态存储区,寄存器存储类别直接送寄存器。变量存储类别定义方法:
存储类别类型变量表;例如:(1)a,b,c为整型自动存储类别变量:autointa,b,c;(2)x,y,z为双精度型静态存储类别变量:staticdoublex,y,z;7.6.2局部变量的存储方式(1)局部变量一般用自动方式存储,用保留字auto加以定义,此时称为自动变量,是动态存储,在函数的调用过程中存在,由编译系统自动处理。例如:
voidf(){autointi,j;autofloatx,y;/*局部变量i,j,x,y以自动方式存储*/
}
…(2)如果希望函数调用完后局部变量的值被保留,不释放其所占存储单元,这时必须将其存储方式定义为静态存储方式,用保留字static加以定义。用静态方式存储的局部变量称为局部静态变量,在函数调用完后其值被保留,由于“局部”的作用域,这个保留的值其它函数并不能使用,只有本函数能够使用。例7-16
计算1到5的阶乘。intjf(n)intn;{staticintf=1;f=f*n;return(f);}main(){inti;for(i=1;i<=5;i++)
printf(″%d!=%d\n″,i,jf(i));}运行结果为:1!=12!=23!=64!=245!=120
每次调用函数jf(i)时,计算出i!,同时该值被保留,可供下次调用函数求下一个阶乘使用,这个保留的值其它函数不能使用。如果函数jf( )中变量f不定义为局部静态变量,则每次调用都被初始化为1,运行结果为:
1!=1 2!=2 3!=3 4!=45!=5(3)如果对数据的读写频繁且存取速度要求较快,C语言允许用寄存器方式直接处理变量,即用保留字register对变量加以描述,此时该变量称为寄存器变量。例如:
longsum(n) intn;
{inti;
registerlongs=0;/*s定义为寄存器变量*/ for(i=1;i<=n;i++) s+=i;
return(s);
}(4)局部变量的缺省存储方式是自动存储方式。7.6.3全局变量的存储方式(1)全局变量一般用外部存储方式存储,用保留字extern加以定义。此时,变量的作用域是构成整个程序的所有程序文件,也就是定义的外部变量可供其它程序文件使用。例如,程序由两个程序文件file1.c与file2.c组成。/*file1.c*/externinta;/*定义extern存储方式变量a*/main(){intpow();intn;intp;scanf(″%d″,&n);p=pow(n);printf(″p=%d\n″,p);}/*file2.c*/externinta;/*申明本文件中使用的是已定义的外部变量a*/intpow(x)intx;{inti,t=1;for(i=1;i<=x;i++)t*=i;return(t);}2)如果希望全局变量仅限于本程序文件使用,而其它程序文件中不能引用,这时必须将其存储方式定义为静态存储方式,用保留字static加以定义。此时称为静态外部变量。例如,在上例文件file1.c中,如果作这样的定义:staticinta;则变量a的作用域被缩小至本程序文件file1.c,文件file2.c中不能引用。
值得注意的是,对全局变量加static定义为静态存储方式,并不意味着这才是静态存储,而不加static是动态存储。两种形式的全局变量(外部变量)其实都是静态存储,都是在编译时分配存储空间的,但作用域不同。使用静态外部变量,有利于隔离错误,有利于模块化程序设计。(3)全局变量的缺省存储方式是外部存储方式。前面章节中的程序没有见到变量的存储类别定义,实际上采用变量的缺省存储方式。对局部变量采用auto方式,对全局变量采用extern方式。1.变量定义的一般形式
存储类别数据类型变量表;2.变量定义的作用①规定了变量的取值范围。②规定了变量进行的运行操作。③规定了变量的作用域。④规定了变量的存储方式。⑤规定了变量占用的存储空间。7.6.4变量小结3.局部变量和全局变量
局部变量:①自动变量,即动态局部变量(离开函数,值就消失).②静态局部变量(离开函数,值仍保留)。③寄存器变量(离开函数,值就消失)。④形式参数可以定义为自动变量或寄存器变量。全局变量:①静态外部变量(只限本程序文件使用)。②外部变量(即非静态的外部变量,允许其它程序文件引用)。4.动态存储和静态存储动态存储:①自动变量(函数内有效)。②寄存器变量(函数内有效)。③形式参数。静态存储:①静态局部变量。函数内有效。②静态外部变量。本程序文件有效。③外部变量。整个程序有效。5.静态存储区和动态存储区
从变量值存放的位置可将变量存储区分为静态存储区和动态存储区:(1)内存中静态存储区: ①静态局部变量。 ②静态外部变量。 ③外部变量。(2)动态存储区:①自动变量。②形式参数。③寄存器变量。
例7-17
仔细阅读程序,注意变量的作用域#,存储类别,分析程序的运行结果。voidf1(x,y){autointi;staticintj;/*调用完,j值保留*/printf("i=%5d,j=%5d\n",i,j);i=x+y;j+=x+y;printf("i=%5d,j=%5d\n",i,j);printf("x=%5d,y=%5d\n",x,y);}/*函数f1尾*/staticintk,l,m;/*在本程序文件内使用*/voidf2(a,b,c)inta,b,c;{k=a+b+c;
l=a*b*c;
m=k+l;
printf("a=%5d,b=%5d,c=%5d\n",a,b,c);
printf("k=%5d,l=%5d,m=%5d\n",k,l,m);}/*函数f2尾*/main( ){intd,e,f,g,h;inti,j,x,y,a,b,c;
printf("请输入d,e,f,g,h:");scanf("%d,%d,%d,%d,%d",&d,&e,&f,&g,&h);printf("第一次调用函数f1( ):\n");f1(d,e);printf("i=%5d,j=%5d\n",i,j);printf("x=%5d,y=%5d\n",x,y);printf("d=%5d,e=%5d\n",d,e);printf("第二次调用函数f1( ):\n");f1(d,e);printf("i=%5d,j=%5d\n",i,j);printf("调用函数f2( )\n");
f2(f,g,h);printf("k=%5d,l=%5d,m=%5d\n",k,l,m);printf("a=%5d,b=%5d,c=%5d\n",a,b,c);printf("f=%5d,g=%5d,h=%5d\n",f,g,h);}运行结果:请输入d,e,f,g,h:1,2,3,4,5第一次调用函数f1( ):i= 34(随机值),j=0i= 3, j=3x= 1,y=2 /*以上在函数f1( )中输出*/i= 34(随机值), j=1985(随机值)x=−19(随机值),y=2340(随机值)d= 1,e=2 /*以上在main( )函数中输出*/第二次调用函数f1( ):i= 34(随机值),j=3i= 3,j=6x= 1,y=2 /*以上在函数f1( )中输出*/i= 34(随机值),j=1985(随机值) /*在main( )函数中输出*/调用函数f2( ):a= 3,b=4, c=5k= 12,l=60,m=72 /*以上在函数f2( )中输出*/k= 12,l=60,m=72a= 13(随机值),b=124(随机值),c=2525(随机值)f= 3,g=4,h=5 /*以上在main( )函数中输出*/7.7结构化程序设计方法7.7.1程序设计基本手段
抽象、枚举与归纳是程序设计的基本手段,也可看作是程序设计的基本原则。程序设计语言与程序本身便是一个抽象。变量的概念是它当前值的抽象,一般地,数据是对要加工处理的客观对象之抽象,各类控制结构是对计算机操作之抽象,算法是对问题的抽象。按此,抽象算法对一些抽象数据实施一系列抽象操作,最终把抽象算法具体化到计算机程序。7.7.2模块化程序设计
一个程序由若干模块组成,函数是C语言中模块的实现工具,较大的模块可用一个程序文件实现。模块组装在一起达到整个程序的预期目的。一个模块只做一个事情,模块的功能充分独立。模块内部的联系要紧密,模块之间的联系要少。模块之间通过接口(形参或外部变量)通讯,模块内部的实现细节在模块外部要尽可能不可见。7.7.3结构化程序的书写和阅读
例7-18
编写程序,求解方程ax2+bx+c=0。算法提示:
(1)方程系数的输入在主函数中完成。
(2)无根、无数根、一个根、相等实根、不等实根、共轭虚根分别用函数rf1( )、rf2( )、rf3( )、rf4( )、rf5( )、rf6( )处理,结果在相应函数中输出。
(3)程序由六个子模块加主控模块组成。程序如下:/*程序7-18,求解方程a*x*x+b*x+c=0*/floatd; /*判别式,全局变量*/main() /*主控程序*/{floata,b,c; /*方程系数*/voidrf1(); /*函数申明*/voidrf2();voidrf3();voidrf4();voidrf5();voidrf6();clrscr();printf("请输入方程系数a、b、c:");scanf("%f,%f,%f",&a,&b,&c);d=b*b−4*a*c; /*计算判别式*/if(a==0)if(b==0)if(c==0)rf1( ); /*调用函数rf1( )解方程0x=0*/elserf2( ); /*调用函数rf2( )解方程0x=c*/elserf3(b,c); /*调用函数rf3( )解方程bx=c*/elseif(d==0)rf4(a,b); /*调用函数rf4( )解方程a*x*x+b*x+c=0,d=0*/elseif(d>0)rf5(a,b,c); /*调用函数rf5( )解方程a*x*x+b*x+c=0,d>0*/elserf6(a,b,c); /*调用函数rf6( )解方程a*x*x+b*x+c=0,d<0*/}voidrf1( ) /*解方程0x=0*/{printf("方程无数解!\n");}
voidrf2( ) /*解方程0x=c*/{printf("方程无解!\n");}
voidrf3(b,c) /*解方程bx=c*/floatb,c;{floatx;x=−c/b;printf("方程的根=%6.2f\n",x);}voidrf4(a,b) /*解方程a*x*x+b*x+c=0,d=0*/floata,b;{floatx;x=−b/(2*a);printf("方程有两个相等实根,根=%6.2f\n",x);}
voidrf5(a,b,c) /*解方程a*x*x+b*x+c=0,d>0*/floata,b,c;{floatx1,x2;x1=(−b+sqrt(d))/(2*a);x2=(−b−sqrt(d))/(2*a);printf("方程第一个根=%6.2f,第二个根=%6.2f\n",x1,x2);}voidrf6(a,b,c)/*解方程a*x*x+b*x+c=0,d<0*/floata,b,c;{floatr,i;
r=−b/(2*a);
i=sqrt(−d)/(2*a);
printf("方程第一个虚根=%6.2f+%6.2fi\n",r,i);
printf("方程第二个虚根=%6.2f−%6.2fi\n",r,i);
}
例7-19
编写正方形、长方形、圆、三角形、梯形、平行四边形等图形的面积计算程序。算法提示:
(1)用六个函数zs( )、cs( )、ys( )、ss( )、ts( )、ps( )完成相应图形的面积计算。
(2)整个程序由这六个子模块加退出模块tc( )及主控模块组成。
(3)为反复计算面积,在主控程序中用永真循环处理。退出由退出模块处理。
(4)由于各图形的特征描述不一,因此将数据输入及输出放各函数中完成。
(5)主控模块是一个典型的菜单程序。程序如下:
/*程序7-19编写通用面积计算程序*/main() /*主控程序*/{charch; /*选择变量*/voidzs(); /*函数申明*/voidcs();voidys();voidss();voidds();voidps();voidtc();while(1){clrscr( ); /*界面显示*/printf("面积计算程序\n");
printf("Z—正方形面积 C—长方形面积\n");
printf("Y—圆面积 S—三角形面积\n");printf("T—梯形面积 P—平行四边形面积\n");
printf("Q—退出程序\n");
printf("请选择操作(Z/C/Y/S/T/P/Q):");
ch=getchar( );
switch(ch) /*选择处理*/{case'z':
case'Z':zs( );break; /*转计算机正方形面积模块*/case'c':
case'C':cs( );break; /*转计算机长方形面积模块*/case'y':
case'Y':ys( );break; /*转计算机圆面积模块*/case's':
case'S':ss( );break; /*转计算三角形圆面积模块*/case't':
case'T':ds( );break; /*转计算梯形面积计算模块*/case'p':
case'P':ps( );break; /*转计算平行四边形模块*/case'q':
case'Q':tc( );break; /*转退出模块*/default:printf("选择错误!");
}}}voidzs( ) /*计算正方形面积*/{floata;floats;printf("请输入正方形的边长:");scanf("%f",&a);s=a*a;printf("所求正方形面积=%7.3f\n",s);}
voidcs( ) /*计算长方形面积*/{floata,b;floats;printf("请输入长方形的长和宽:");scanf("%f,%f",&a,&b);s=a*b;printf("所求长方形面积=%7.3f\n",s);}#definePI3.14159voidys( ) /*计算圆面积*/{floatr;
floats;
printf("请输入圆的半径:");
scanf("%f",&r);
s=PI*r*r;
printf("所求圆面积=%7.3f\n",s);
}voidss( )/*计算三角形面积*/{floata,b,c;
floatl;
intflag;
floats;
printf("请输入三角形的三边:");
scanf("%f,%f,%f",&a,&b,&c);
flag=(a+b>c)&&(b+c>a)&&(c+a>b);
if(flag) {l=0.5*(a+b+c);
s=sqrt(l*(l−a)*(l−b)*(1−c));
printf("所求三角形面积=%7.3f\n",s);
}elseprintf("不能构成三角形!\n");}voidts( )/*计算梯形面积*/{floata,b,h;
floats;
printf("请输入梯形的上底,下底,高:");
scanf("%f,%f,%f",&a,&b,&h);
s=(a+b)*h*0.5;
printf("所求梯形面积=%7.3f\n",s);
}voidps( ) /*计算平行四边形面积*/{floata,h;
floats;
printf("请输入平行四边形的底和高:");
scanf("%f,%f",&a,&h);
s=a*h;
printf("所求平行四边形面积=%7.3f\n",s);}voidtc( ) /*退出程序*/{printf("退出面积计算程序!");
exit(0);
}
例7-20
找出100~1000之间的素数、完数、回文数、水仙花数等特殊数。算法提示:
(1)定义四个函数ss( )、ws( )、hs( )、shs( )求出相应的特殊数。
(2)主程序仅包含函数ss( )、ws( )、hs( )、shs( )的调用。整个程序结构清晰,可读性极好。程序如下:/*程序7-20,计算100~1000之间的特殊数*/main() /*主程序*/{voidss(); /*函数申明*/voidws();voidhs();voidshs();printf(″计算100~1000之间的特殊数\n″);ss(); /*调用函数ss()、ws()、hs()、shs()求出相应特殊数*/ws();hs();shs();}voidss() /*计算100~1000之间的素数*/{inti,j;intp;printf(″100~1000之间素数如下:\n″);for(i=100;i<=1000;i++)
{for(p=1,j=2;j<=i;j++)
if(i%j==0){p==0;break;}if(p)printf(″%8d″,i);}printf(″\n″);}voidws( ) /*找出100~1000之间的完数*/{inti,j;
ints;
printf("100~1000之间的完数为:\n");
for(i=100;i<=1000;i++){for(s=0,j=1;j<i;j++)if(i%j==0){s+=j;break;}if(s==i)printf("%8d",i);
}printf("\n");}voidhs( ) /*找出100~100
温馨提示
- 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
- 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
- 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
- 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
- 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
- 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
- 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。
最新文档
- 中医院中药处方智能审核制度
- 智能办公效率提升培训手册
- 园区安防监控智能化系统工程方案设计
- 信息安全管理委员会工作规范
- 医院检验科试剂入库验收制度
- 医院病房改造无线网络覆盖设计
- 压力管道安全风险辨识清单
- 污水处理工程施工质量验收规范手册
- 现代物流管理专业人才培养方案
- 危险废物岗位操作规程
- 电梯安全管理人员管理和培训制度
- 《成人间歇性经口至食管管饲技术要求》
- (正式版)DB65∕T 4733-2023 《石化行业雷电灾害隐患排查指南》
- 造纸技术的演变
- 学堂在线 庄子哲学导读 章节测试答案
- JG/T 194-2006住宅厨房、卫生间排气道
- 23G409先张法预应力混凝土管桩
- 幼儿园课件之大班数学《大家爱锻炼》
- 社区家庭护理(社区护理学课件)
- 期中素能测评(B)课件英语七年级上册
- DZ∕T 0341-2020 矿产地质勘查规范 建筑用石料类(正式版)
评论
0/150
提交评论