x:
y;//比较两个数的大小,将较小的数存于c变量中
return(c);//将较小的数返回主函数
}
doublearea(intr)//函数定义,求半径为较小的数的圆面积,将求出的面积返回主函数
{doublepi=3.1415926;//圆周率
doubleg;//g用来存放圆的面积
g=pi*r*r;//求出圆的面积лr2(即лr的平方),存于变量g中
return(g);//将圆的面积g返回主函数
}
运行结果:
输入2个数:
5,8
最小的数5为半径,求出的圆面积为:
78.539815
注意:
输入的2个数中间要用“,”隔开。
4.2函数的定义
函数是一个完成特定工作的独立程序模块,分库函数和自定义函数两种。
库函数由系统提供定义,编译时只要直接调用即可,而象前面的area()和max()函数,是程序人员自己定义的,属于自定义函数。
一个较大的程序一般应分为若干个程序模块,每一个模块用来实现一个特定的功能。
所有的高级语言中都有子程序这个概念,用子程序来实现模块的功能。
在C语言中,子程序的作用是由函数来完成的。
一个C程序可由一个主函数和若干个函数构成。
由主函数调用其它函数,其它函数也可以互相调用。
同一个函数可以被一个或多个函数任意调用多次。
函数返回有两种情况:
(1)函数经过运算,得到一个明确的运算结果,并需要回送该结果。
例如,前面的max()函数要将比较出的小的数返回主函数调用它的地方,用来求圆的面积。
(2)函数只是完成一系列操作步骤,不需要回送任何运算结果。
1、返回运算结果的函数定义
函数类型函数名(形参表)//函数首部
{//函数体
函数实现过程;
return表达式;
}
函数的定义包括函数首部和函数体两部分,其中函数首部由函数类型、函数名和形参表组成;函数体包括一对大括号内的若干条语句,体现函数的实现过程,并用return语句返回运算的结果。
return语句经常是最后一条语句。
return语句只能返回一个值。
2、不返回结果的函数定义
void函数名(形参表)//函数首部
{//函数体
函数实现过程;
}
函数类型为void表示不返回结果,函数体中可以出现return语句,也可以省略。
void类型的函数虽然不直接返回一个值,但它的作用通常以屏幕输出等方式体现。
例4、2输出5之内的数字金字塔
#include
intmain()
{voidpyramid(intn);//声明输出数字金字塔的函数
pyramid(5);//调用输出数字金字塔的函数
return0;
}
voidpyramid(intn)//函数定义
{inti,j;
for(i=1;i<=n;i++)//需要输出的行数
{for(j=1;j<=n-i;j++)//输出每行左边的空格
printf("");
for(j=1;j<=2*i-1;j++)//输出每行的数字
printf("%d",i);
putchar('\n');
}
}
运行结果:
1
222
33333
4444444
555555555
如果将函数pyramid(intn)的最后一个for循环:
for(j=1;j<=2*i-1;j++)
printf("%d",i);
改成:
for(j=1;j<=i;j++)
printf(“%d”,i);注意:
%d后面有一个空格。
那么运行结果是:
1
22
333
4444
55555
该函数的功能很明显,就是在屏幕上输出数字金字塔,不做任何运算,也没有运算结果,自然也不需要返回值。
函数定义时,形参n决定了需要输出的数字金字塔的层数。
voidpyramid(intn)函数中省略了return语句,因为它无须返回什么。
4、3函数的调用
定义一个函数后,就可以在程序中调用这个函数。
调用函数时,将实参传递给形参并执行函数定义所规定的程序过程,以实现相应的功能。
在C语言中,调用标准库函数时,只需要在程序的最前面用#include命令包含相应的头文件;如:
要进行数学运算须包含#include。
调用自定义函数时,程序中必须有与调用函数相对应的函数定义,如:
例4.2中的pyramid(5);和voidpyramid(intn)。
1、参数传递
函数定义中的参数称为形参,函数调用时的参数称为实参。
形参和实参必须一一对应,要求两者数量相同,类型一致。
在程序运行中,遇到函数调用时,将实参的值依次传给形参,这就是参数传递。
例4.2中pyramid(5)函数的实参5传递给voidpyramid(intn)函数形参的n,这时n就是5。
2、函数结果返回
从函数返回值的类型看,有两种类型:
一种是完成确定的运算,有一个运算结果返回给主调函数,如例4.1。
另一种是完成指定工作,没有确定的运算结果需返回给主调函数,通常用于实现结构化程序设计中的过程模块,函数类型用void指定,如第三章例3.8,一般最后用return;不返回到主调函数的什么地方。
下面讨论第一种类型,即有结果返回的函数。
例4、3判断一个数是奇数还是偶数,程序设计思想是在函数中判断若是偶数返回1,否则返回0。
#include
intmain()
{ints,t;
inteven(intn);
scanf("%d",&s);
printf("输入的数是:
%d\n",s);
t=even(s);
printf("经奇偶判别后,结果是t=%d\n(若t=1是偶数,若t=0是奇数)\n",t);
return0;
}
inteven(intv)
{if(v%2==0)
return1;
else
return0;
}
运行结果:
(在终端上输入)9
(在终端上显示)输入的数是:
9
经奇偶判别后,结果是t=0
若t=1是偶数,若t=0是奇数。
说明:
如果你在终端上输入8,那么t就等于1,说明是偶数。
函数中出现了两个return语句,执行时根据条件选择其中一个,它们的作用相同,即结束函数运行,并回送结果。
如果return语句后面还有其它语句,将不会被执行。
3、函数原型声明
例4.2和4.3都出现了函数声明,这是因为C语言要求函数先定义后调用,将主调函数放在被调用函数的后面,就象变量先定义后使用一样。
如果自定义函数被放在主调函数的后面,就需要在函数调用前加上函数原型声明。
如果在调用函数前,即不定义,也不声明,程序编译时会出错。
函数声明的目的主要是说明函数的类型和参数的情况,以保证程序编译时能判断对该函数的调用是否正确。
函数声明的一般格式为:
函数类型函数名(参数表);
即只写函数定义中的第一行(函数首部),并以分号结束。
函数声明是一条C语句,而函数定义时的函数首部不是语句,后面不能跟分号。
在编写程序时,一般都把主函数main()写在最前面,这样做的好处是使整个程序的结构和功能开门见山地呈现在读者面前。
通过函数声明,可以解决函数先调用后定义的矛盾。
4.4使用函数编写程序
例4.4输入精度e,使用格里高利公式求п的近似值,精确到最后一项的绝对值小于e。
要求定义和调用函数funpi(e)求п的近似值。
格里高利公式:
п111
—=1-—+—-—+…
4357
程序编制如下:
#include
#include
intmain()
{doublee,pi;//e代表精度
doublefunpi(doublee);//声明funpi,计算pi函数
printf("Enter(请输入计算精度)e:
");//提示用户输入精度,精确到小数点后多少位。
scanf("%lf",&e);//输入的精度放入变量e中
pi=funpi(e);//调用函数,把返回值赋给pi。
即pi值
printf("pi=%f\n",pi);//打印出п(pi)的值
return0;
}
doublefunpi(doublee)//定义计算pi的函数
{
intdenominator,flag;//denominator是分母,flag是符号
doubleitem,sum;//item存放第i项的值,sum存放累加和即最终求到的п的值
flag=1;//flag表示第i项的符号,初值为1
denominator=1;//denominator表示第i项的分母,初值为1
item=1.0;//item中存放第i项的值,初值取1
sum=0;//置累加和pi的初值为0
//当|item|>=e时,执行循环
while(fabs(item)>=e)
{item=flag*1.0/denominator;//计算第i项的值
sum=sum+item;//累加第i项的侄1
flag=-flag;//改变符号,为下一次循环做准备
denominator=denominator+2;//分母递增2,为下一次循环做准备
}
returnsum*4;//将左边的分母4乘到右边就得到л的值。
}
运行结果:
Entere:
0.00000001
pi=3.141593//机器打印出最多8位,包括小数点
例4.5输入任意一个数m,求m以内的全部素数,每行输出10个。
要求定义和调用函数prime(m),判断m是否为素数,当m为素数时返回1,否则返回0。
素数就是只能被1和自身整除的正整数,1不是素数,2是素数。
判断一个数m是否为素数,需要检查该数是否能被1和自身以外的其它数整除,即判断m是否能被2~m-1之间的整数整除,用求余运算%来判断整除,余数为0表示能被整除,否则就意味着不能被整除。
例如,9%3得0,说明9能被3整除;而7%2得1,说明7不能被2整除。
设i取值[2,m-1],如果m不能被该区间上的任何一个数整除,即对每个i,m%i都不为0,则m是素数;但是只要m能被该区间上的某个数整除,即只要找到一个i,使m%i为0,则m肯定不是素数。
由于m不可能被大于m/2的整数除,所以上述i的取值区间可缩小为[2,m/2],数学上能证明,该区间还可以是[2,√m]。
程序设计如下:
#include
#include//求平方根,需要包含数学库函数
intmain()
{intcount,m,n;//count记录素数的个数,m是从2开始的除数,n是输入的数
intprime(intm);//声明判断是否为素数的函数
count=0;//count记录素数的个数,用于控制输出格式
printf("请随意输入一个数:
");//输入提示
scanf("%d",&n);//输入的数放入变量n
for(m=2;m<=n;m++)
{//注意if语句的表达式里:
非0为真,0为假
if(prime(m))//调用prime(m)函数,判断m是否为素数,若返回1即m%i不为0是”真”
{printf("%6d",m);//输出素数m,每个输出的素数都占6位
count++;//累加已经输出的素数个数
if(count%10==0)printf("\n");//如果count是10的倍数,就换行
}
}
printf("\n");//最后将打印头放在下一行
return0;
}
//定义判断素数的函数,如果x是素数则返回1(真);否则返回0(假)。
intprime(intm)
{inti,t;//i是循环次数,t是最大的能做m除数的数
if(m==1)return0;//1不是素数,返回0
t=sqrt(m);//求m的平方根
for(i=2;i<=t;i++)//循环从2到t次
if(m%i==0)return0;//如果m不是素数返回0
return1;//m是素数,返回1
}
例4.6造一个函数表
#include
#include
main()
{
doubleq,w,r,t,x;
doublesin(double),cos(double),tan(double),exp(double);//exp指数函数
printf("xsin(x)cos(x)tan(x)exp(x)\n");
for(x=0.0;x<=1.5708;x=x+0.15708)
{q=sin(x);
w=cos(x);
r=tan(x);
t=exp(x);
printf("%f%f%f%f%f\n",x,q,w,r,t);
}
for(x=1.5708;x<=1.5708;x=x+0.15708)
{q=sin(x);
w=cos(x);
r=tan(x);
t=exp(x);
printf("%f%f%f%.1f%f\n",x,q,w,r,t);
}
for(x=1.72788;x<=3.14;x=x+0.15708)
{q=sin(x);
w=cos(x);
r=tan(x);
t=exp(x);
printf("%f%f%f%f%f\n",x,q,w,r,t);
}
}
例4.7验证(不是证明)哥德巴赫猜想
1742年德国数学家哥德巴赫给当时住在德国的数学家欧拉的一封信中,提出了把一个整数表示成两个素数之和的推测,对此我们略加修改说成:
任意一个充分大的偶数(大于等于6),总可以分解为两个素数之和,这便是著名的哥德巴赫猜想,简称1+1。
二百多年过去了,对于这个问题的证明(而不是验证),使许多数学家为之却步,但在我国,对于这个问题的研究一直处于国际领先地位。
我国数学家陈景润取得了到目前为止的最好成就,证明了1+2。
下一步也就是最后一步,证明1+1。
看谁能摘取被誉为王冠上的这颗明珠。
下面我们用计算机来验证(不是证明)。
基本思想是,对于给定的数,先确定一个素数,然后用那个偶数减去这个素数,从而判断其差是否也是素数,若是,则找到了答案,若不是,就确定另外的素数。
重复以上的步骤,我们采用键盘输入方式,随意敲入一个充分大的偶数N,来验证1+1。
如果一个偶数可以分解成多组两个素数之和,则取最小的素数和最大的素数之和那一组。
比如:
30=13+17=11+19=7+23,则取:
7+23这组。
程序的编写是这样的:
#include
ints(intv)
{intj;
for(j=2;jif(v%j==0)//若v是3,j是2则v%j不等于0,而到下一条语句返回0
return(-1);//v%j等于0返回-1,不是素数
return(0);//不等于0,是素数
}
voidmain()
{intn,a,b,d,k;//n是给定的数,a是从6开始的偶数判断,b做为开始的最小素数,d是a-b的值
printf("请输入一个大于等于6的充分的偶数N=?
:
\n");//判它是否为素数,k用做换行
scanf("%d",&n);
printf(“\n”);
printf("看到6-%d之间的各个偶数分成两个素数之和是(取最小的素数和最大的素数之和那一组):
\n\n",n);
k=0;
for(a=6;a<=n;a+=2)//从6开始,每次+2保证是偶数,一直加到小于等于你敲入的那个数n为止
{//先确定一个素数3,每次+2,一直加到小于等于a/2为止,若敲入6则a不加还是6
for(b=3;b<=(a/2);b+=2)//若a是6,则b也不加,还是3
{if(s(b)==0)//将b带到子函数看是否为0,子函数若返回0是素数,则执行下面语句
{d=a-b;//若敲入n是6,a也是6,则b为3,那么d就是3
if(s(d)==0)//显然若敲入6,这里d是3,到子函数判别后返回是0
{k++;//k是表示第多少行
//若敲入的是6,a就是6,b是3,d是3,打印出6=3+3
printf("%d=%d+%d\t",a,b,d);//打印6=3+3
if(k%5==0)//打印每5组素数之和,换一行。
printf("\n");//回车换行
break;//跳出循环,结束本循环
}
}
}
}
printf("\n\n");//将打印头放在下一行的最前面
return;
}
运行结果:
请输入一个大于等于6的充分的偶数N=?
:
6(若敲入的数是6)
6-6之间的各个偶数分成两个素数之和是(取最小的素数和最大的素数之和那一组):
6=3+3
运行结果:
请输入一个大于等于6的充分的偶数N=?
:
30(若敲入的数是30)
6-30之间的各个偶数分成两个素数之和是(取最小的素数和最大的素数之和那一组):
6=3+38=5+310=3+712=5+714=3+11
16=3+1318=5+1320=3+1722=3+1924=5+19
26=3+2328=5+2330=7+23
若敲入30,则n=30
第一个循环条件a<=n681012141618202224262830
第二个循环条件b<=a/23579111315
d=a-b即d=30-3=27而27不是素数则b+2
即d=30-5=25而25不是素数则b+2
即d=30-7=2323是素数这时a=30b=7d=23
屏幕上就打印出30=7+23
而对于中间的11+19,13+17就没设打印出来,因为用a减去第一个最小的素数得到的数若为素数(就是最大的素数)就直接打印了,所以只能是打印最小的素数和最大的素数那一组。
说明:
1、敲入任何一个大于等于6的数都能将这个数和6之间的偶数按一排5(用k来控制)列和按最小素数和最大素数相加之和的形式打印出来,你可敲入50、51、200试一试。
2、该程序的编写是将子函数s(intv)放在了程序的前头,这样做可以在主函数mait()里省略对子函数s(intv)的声明。
3、i++与++i的区别
(1)++i<6
这个表达式将i+1与6比较;
i++<6
这个表达式将i与6比较。
但两个表达式计算完毕后,i的值都会在原基础上加1。
第一种情况++i<6中,当i的值为5时,i+1不小于6,循环终止,i自加为6.
第二种情况i++<6中,当i的值为5时,i小于6,循环继续,i自加为6,此时j=5,i大于j,跳过j--这步进入循环条件判断,此时i的值为6,i不小于6,循环终止,i自加为7.
例如:
#include#include
main()main()
{inti=1,j=10;{inti=1,j=10;
do{do{
if(i>j)continue;if(i>j)continue;
j--;j--;
}while(++i<6);}while(i++<6);
printf(“i=%dj=%d\n”,i,j);printf(“i=%dj=%d\n”,i,j);
}}
左式输出i=6,j=5右式输出i=7,j=5
4.5变量与函数
1、局部变量
迄今为止,在程序中使用的变量都定义在函数的内部,它们的有效使用范围也被局限于所在的函数内。
C语言中把定义在函数内部的变量称为局部变量。
局部变量也称自动变量。
自动变量的定义形式为:
auto类型名变量表;例如:
autointx,y;在自动变量定义时,auto可以省去,其形式与以前定义的普通变量完全相同,即前面定义的局部变量都是自动变量。
使用局部变量可以避免各个函数之间的变量相互干扰,尤其是同名变量。
定义在两个不同函数中的局部变量尽管同名,但它们是两个不同的变量实体,有各自的存储单元和使用范围,两者不会相互干扰。
C语言的这个特性在结构化程序设计中非常有用,它可以保证函数的独立性,减少相互间的干扰。
除了作用于函数的局部变量外,C语言还允许定义作用于复合语句中的局部变量,其有效使用范围被局限于复合语句内。
例4.7在复合语句中定