第六讲 函 数
知识要点
6.1 概述
6.2 函数的定义
6.3 函数的返回值
6.4 函数的调用
6.5 函数的递归调用
6.1 概述
C程序是由一个主函数和其它若干函数构成,每个函数实现一定的功能,其中主函数main()是必需的,其它函数被主函数调用或者其它函数之间相互调用。C语言的函数可以分为三类:主函数main()、库函数(如printf()、scanf()等)和用户自定义函数。
C程序总是从主函数开始执行,其它函数只有在被主函数或其它正在执行的函数调用时才能被程序执行,执行后返回调用函数,最后返回到主函数,在主函数中结束整个程序的运行。
所有的函数在定义时是相互独立的,它们之间是平行关系,所以不能在一个函数内部定义另一个函数,即不能嵌套定义。函数间可以互相调用,但是不能调用主函数。
void main()
{ int year,month,date,days,n;
printf("please input year,month,date:");
scanf("%d,%d,%d",&year,&month,&date);
days=len_of_days(year,month,date);
printf("%d/%d/%d is the %d in %d\n",year,month,date,days,year);
}
6.2 函数的定义
常见的函数定义的形式如下:
类型标识符 函数名(形式参数类型说明表列)
{
函数体
}
double sqrt( double x );
例如求两个浮点数x、y中最大数的函数max()定义
float max(float x,float y)
{float z;
if (x>y) z=x;
else z=y;
return z;
}
类型标识符为函数的类型,与return语句返回值的类型相同,可以理解为函数最终的结果的类型。它可以是任何一种有效的类型,当函数类型标识符缺省时默认是整型。上例中函数max()的结果为两个浮点数的最大数,即同样是浮点数类型,其返回值z也是浮点类型,所以函数的类型标识符为float。如果函数无返回值时,类型标识符为void。
函数名要符合C语言规定的标识符的命名规则,函数名字必须唯一,不能与函数体内变量或形式参数名相同。所以上例中如果变量z改为max将导致结论错误。
形式参数类型说明表中的形式参数用于接受主调函数传递过来的数值。
例如max()是求两个数的最大数,需要从主调函数中传过来两个浮点数,所以形式参数有两个。如果max()改为求三个数的最大数,则从主调函数中需要传过来三个数,此时形式参数需要定义三个,并且形式参数的类型必须与传过来数据类型一致。
形式参数的命名只要符合变量的命名规则即可,无需与主调函数中的变量名一致。
如果函数不需要从主调函数处接受数据,可以不带形式参数,此时形式参数类型说明表是空的,但是函数名后面的圆括号不能省。
例6.1 显示如下图形:
******
******
******
******
******
******
******
#include <stdio.h>
void star()
{printf(“******\n”);}
void main()
{ int i,j;
for (i=1;i<=4;i++)
{ for(j=1;j<=2*i-1;j++) printf (“ “);
star(); }
for (i=3;i>=1;i--)
{ for(j=1;j<=2*i-1;j++) printf (“ “);
star(); }
}
在形式参数类型说明时,一定要注意:每一个形式参数必须单独说明类型,形式参数说明之间用逗号间隔。
float max(float x,y)”或者“float max(float x;float y)”是错误的。
函数体也可以是空的,例如:
void input()
{ }
此函数为空函数,什么都不做,没有任何实际作用。在主调函数中对应语句为“input();”。
它主要用于在编写程序时留的空缺,当需要添加该函数功能时补上内容即可。空函数的使用对设计程序或者调试都极为方便。
函数的定义也可以采用下列形式:
类型标识符 函数名(形式参数表列)
形式参数类型说明
{
函数体
}
这种形式是一种传统的风格,前面介绍的方法是现代风格的函数定义,这两种方法都允许使用,但是ANSI新标准推荐的是第一种方法。
float max(x,y)
float x,y;
{ float z;
if (x>y) z=x;
else x=y;
return z; }
6.3 函数的返回值
在调用函数时,有时需要将运算结果返回主调函数,此时需要使用return语句返回一个值,称作函数返回值。
return语句形式: return (表达式);
例如: return z; return 0;return (x>y?x:y);
float max(float x,float y)
{float z;
if (x>y) z=x;
else z=y;
return z; }
float max(float x,float y)
{return (x>y?x:y); }
int is_leap_year(int year)
{
if (year%4==0&&year%100!=0||year%400==0)
return (1);
else return(0);}
函数返回值的类型应该与函数类型一致,如果不一致时,函数类型决定返回值的类型。
函数无返回值时,函数类型说明为void。
例如:
void star()
{printf(“******\n”);}
6.4 函数的调用
函数只有在被调用时才能执行。当一个函数调用另一个函数时,程序就从主调函数转移到被调函数,并且从被调函数的函数体起始位置开始执行,直到函数体执行结束,返回到主调函数的调用位置继续往下执行。
函数调用的一般形式
函数名(实参表列);
当实参表列中含有多个参数,则各参数用逗号间隔
例如max(a,b)。
实参表列也可以没有,但括号不能省略,如例6.1中star()。
float max(float x,float y)
{return (x>y?x:y); }
实参和形参必须个数相等、类型一致、顺序对应,进行数据的“值传递”。
例1.3中,主调函数出现max(a,b),用户自定义函数为float max(float x,float y),则执行到c=max(a,b)时,实参a的值传给形参x、实参b的值传给形参y,这样x、y等于a的值和b的值。
#include <stdio.h>
float max(float x, float y)
{float z;
if (x>y) z=x;
else z=y;
return z; }
void main( )
{float a,b,c;
printf("Please input two numbers (a,b):");
scanf("%f,%f",&a,&b);
c=max(a,b);
printf("%f,%f,the max is %f\n",a,b,c); }
6.4 函数的调用
实参和形参必须个数相等、类型一致、顺序对应,进行数据的“值传递”。
#include <stdio.h>
void change(int x,int y)
{ int t;
printf (“x=%d,y=%d\n”,x,y);
t=x; x=y; y=t;
printf (“x=%d,y=%d\n”,x,y);}
void main()
{ int a=3,b=4;
printf (“a=%d,b=%d\n”,a,b);
change(a,b);
printf (“a=%d,b=%d\n”,a,b); }
特别要注意的是:实参和形参之间是“单向的值传递”,即a、b的值可以传给x、y,而x、y的值不能传给a、b,x、y的改变也不影响a、b。
实参可以是常量、变量、表达式或函数。
#include <stdio.h>
void main()
{ float min(float x,float y); /*函数定义在调用的函数之后,要先声明*/
float a=3.9,b=4.1;
printf (“%.1f\n”,min(a,b));
printf (“%.1f \n”,min(5,a*b));
printf (“%.1f \n”,min(b-1,min(a,b)));
}
float min(float x,float y)
{ return (x<y?x:y); }
函数同变量一样,在调用前应该在主调函数中事先说明,即“声明”。
声明的方法是在主调函数开始位置加上被调函数的“函数原型”,即函数定义的第一行。
上一例中main()函数体内添加“float min(float x,float y);”
它还可以省略为“float min(float,float);”或者“float min()”。
下面两种情况可以不用声明:
当被调用函数的定义的位置在主调函数之前
被调用函数是整型int。
所以为了避免声明问题,我们在前面的例子几乎都将函数的定义放在主函数的前面。
C语言中函数不能嵌套定义,但是可以嵌套调用,即在调用一个函数的过程中可以又调用另一个函数。
例5.11中用户自定义函数嵌套调用的示意图。
示例
int max(int x,int y)
void main( )
{ int a,b,c;
scanf(“%d,%d”,&a,&b);
c=max(a,b);
printf(“Max is %d”,c);
}
int max(int x,int y)
{ int z;
if(x>y) z=x;
else z=y;
return z;
}
/*--------------------------------------------------------------
程序L1_3.C :输入 a、b两个数,输出其中最大数
---------------------------------------------------------------*/
#include <stdio.h> /* 包含输入输出头文件 */
float max(float x, float y) /* 定义求x、y的最大值的函数max*/
{float z; /* 定义实数变量z*/
if (x>y) z=x; /* 如果x大于y,则z等于x */
else z=y; /* 否则z 等于y */
return z; /* 返回z, 则max函数值为z的值 */
}
main( ) /* 定义主函数 */
{float a,b,c; /* 定义实数变量a、b、c */
printf("Please input two numbers (a,b):");
scanf("%f,%f",&a,&b); /* 从键盘输入a、b */
c=max(a,b); /* c等于a、b的最大数 */
printf("%f,%f,the max is %f\n",a,b,c); }
}
输入年、月、日,输出该日期是该年的第几天
/*---------------------------------------------------------
程序L5_11.C功能:输入年、月、日,输出该日期是该年的第几天
---------------------------------------------------------*/
#include <stdio.h>
/*函数is_leap_year()判断年year是否是闰年,如果是返回值为1,否则为0*/
int is_leap_year(int year)
{ int leap;
if (year%4==0&&year%100!=0||year%400==0) leap=1;
else leap=0;
return leap;}
/*函数len_of_month()的返回值为某年year的某月month的天数*/
int len_of_month(int year,int month)
{ int month_days;
if (month==2)
if (is_leap_year(year)) month_days=29;
else month_days=28;
else if (month==4||month==6||month==9||month==11) month_days=30;
else month_days=31;
return month_days;}
/*函数len_of_days()的返回值为该日期date是该年year的第几天*/
int len_of_days(int year,int month,int date)
{ int total_days,n;
for(n=1,total_days=0;n<month;n++)
total_days+=len_of_month(year,n);
total_days+=date;
return total_days;}
6.5 函数的递归调用
C语言的函数调用允许直接或间接地调用该函数本身,称为函数的递归调用。
递归调用函数的使用可以解决具有递归性质的问题
已知n!的数学表示方法是:
由公式可知n!可以由(n-1)!表示。令an=n!,则an= n*an-1,即第n项的值可以由它的第n-1项固定公式来表示,凡是可以化成这种定义方法an=f(an-1)的,都可以采用递归调用。例如1+2+…+…+n可以表示为an=an-1+n。
实际上只要第n项的值可以由它前面的一项或几项固定地表示,就可以适用递归算法。例如Fibonacci数列的通项为an=an-1+an-2,也可以使用递归算法。
在编写递归算法的时要特别注意:递归调用必须可以满足一定条件时结束递归调用,否则无限地递归调用将导致程序无法结束。
例如n!中当n=1时,令结果为1则可以结束递归调用,否则它将继续调用(0)!而无限继续下去。
对于由多个通项表示的情况,如Fibonacci数列,则需要给出最后一个递归调用的所有项的值,即直接给出n=1时的结果以及n=2时结果才可以结束递归调用。
函数的递归调用
#include <stdio.h>
long fac(int n)
{ if (n= =1) return 1L; /*“1L”为长整型常量*/
else return n*fac(n-1);}
void main()
{int m;
scanf (“%d”,&m);
printf("%2d!=%d\n",m,fac(m));}
举一反三 :使用递归算法编写计算 1+2+3+…+n的函数。
Fibonacci数列
某人买回一对小兔,一个月后小兔长成大兔。再过一个月,大兔生了一对小兔,以后,每对大兔每月都生一对小兔,小兔一个月后长成大兔。如此下去,问一年后此人共有多少对兔子
按这个规律,可以把兔子数一直写下去:1,1,2,3,5,8,13,21,34,55,89,144,233,377,610,……。
若记第n个月的兔子数为fn,就有
f0+f1=f2,f1+f2=f3,f2+f3=f4……
一般的,有fn-2+fn-1=fn。
fac(int x)
{ int result;
if (x==1||x==2) result=1;
else result=fac(x-1)+fac(x-2);
return result;
}
作业与上机练习
- 写一个函数int prime(int x),如果x是素数返回值为1,否则返回0。并用主函数验证它。
2.写一个函数power(double x, int n),其返回值为xn,并用此函数计算1.53。
3.求1到10共10个数中取出3个不同的数,共有多少种组合方式?
算法:使用数学中的组合公式,其中m=10,n=3。
4.用递归算法编写求Fibonacci数列第n项值的函数fib(int n),并用主函数输出它的前20项来验证该函数
思考题
用递归的方法输出回文:
如输入 c
则输出:cbabc
ps:此教程配套ppt下载地址如下,如果需要可以下一下,不过其实也无所谓,这篇文章就是全部文本。
https://download.csdn.net/download/qqhxmdq/86914157
全部课程的PPT已经整理好了,需要的童鞋可以点击下载
总-自己使用的C语言教程PPT-适合老师备课或者自学-15篇PPT合集.zip-C文档类资源-CSDN下载