450 likes | 605 Views
本章主要内容. 第六章 函 数. 函数的定义 函数的调用 函数的嵌套调用 递归函数 数组与函数 全局变量和局部变量. C 程序的结构由若干个 C 函数组成。 C 函数是 C 程序的组成部分,是由完成一特定任务的说明语句和执行语句组成的基本功能单元。 C 函数的功能相当于其它程序设计语言中的一个程序模块,或一个子程序。. 6.1 认识C函数. 学生成绩管理系统. 成绩录入. 成绩管理. 打印输出. 成绩更新. 成绩查询. 插入. 更新. 删除. 按学号查询. 按课程查询. 模块化程序示意图 :. 图 6-1 学生成绩管理系统.
E N D
本章主要内容 第六章 函 数 • 函数的定义 • 函数的调用 • 函数的嵌套调用 • 递归函数 • 数组与函数 • 全局变量和局部变量
C程序的结构由若干个C函数组成。 C函数是C程序的组成部分,是由完成一特定任务的说明语句和执行语句组成的基本功能单元。 C函数的功能相当于其它程序设计语言中的一个程序模块,或一个子程序。 6.1认识C函数
学生成绩管理系统 成绩录入 成绩管理 打印输出 成绩更新 成绩查询 插入 更新 删除 按学号查询 按课程查询 模块化程序示意图: 图6-1 学生成绩管理系统
函数调用程序实例 调用函数 main( ) { printstar(); print_message( ); printstar( ); } printstar( ) { printf(“**************\n”);} print_message( ) { printf(“how do you do!\n”);} 用户自定义函数
函数的分类(1) • 从使用角度分: 标准库函数:以程序库的形式直接提供给用户使用。 stdio.h……gets()、printf() math.h ……sin()、sqrt() 用户自定义函数:由用户自己建立定义。
函数的分类(2) • 从函数形式分: • 无参函数:调用函数时主函数不将数据传送给被调函数。用于完成特定功能的操作。 如:c=getchar( )…… • 有参函数:调用函数时在主调函数和被调函数之间有参数传递。 如:putchar(c)、puts(str)、……
函数说明 • 一个C程序是由一个或多个源文件组成,每个源文件由若干函数组成。 • C程序的执行从main函数开始,也从main函数终止。 • 源程序中所有的函数在结构上都是平行的,互相独立的。 • main函数可以调用其他函数,其他函数之间可以互相调用。
函数的定义(一) • 无参函数的特点:只完成某些动作,不进行参数传递。 • 无参函数的定义形式: 类型标识符 函数名( ) { 说明部分; 语句部分 }
例:无参函数使用示例。 main( ) { printstar( ); printword( ); printstar( ); } printstar( ) /*定义无参函数printstar()*/ { printf("******************\n");} printword( ) /*定义无参函数printword()*/ { printf("This is an example\n");}
有参函数定义形式(一): 类型标识符 函数名(形式参数列表) 形式参数说明; { 说明部分; 语句部分; } int max(x,y) int x,y; { int z; z=(x>y)?x:y; return(z); } 函数的定义(二)
int max( int x,int y) { int z; z=(x>y)?x:y; return(z); } 有参函数定义形式(二): 类型标识符 函数名(类型名 参数1,…,类型名 参数n) { 说明部分; 语句部分; } 函数的定义(二)
有参函数定义说明: • “类型标识符”指的是函数计算结果的数据类型,即函数类型。 • “函数名”必须是合法的标识符,命名时尽可能使之“见名知意”。 • 参数的类型定义部分可在{ }外,也可在{ }内。 • 只在函数内部使用的变量,因为不参与主调函数与被调函数之间的参数传递,必须放在{ }内部的说明部分位置。
例:求两数的最大值。注意函数的定义形式。 main( ) { int a,b,c; scanf("%d%d",&a,&b); c=max(a,b); printf("max=%d\n",c); } int max(x,y) int x,y; { int z; z=(x>y)?x:y; return(z); }
函数参数 几个相关术语: • 函数调用:一个函数调用另一个函数。 • 主调函数、被调函数 • 有参函数:主调函数和被调函数之间有数据传递关系,被调函数即有参函数。 • 无参函数:主调函数和被调函数之间无数据传递关系,被调函数即无参函数。
关于实参和形参 main( ) {int a,b,c; scanf("%d%d",&a,&b); c=max(a,b); printf("max=%d\n",c); } int max(int x,int y) { int z; z=(x>y)?x:y;return(z);} 实际参数 形式参数
函数的返回值 • 函数的返回值是通过return语句传递的。 • return语句的一般形式: return 表达式; return (表达式);
main() { int a,b,c; scanf(“%d%d”,&a,&b); c=max(a,b); printf(“max=%d\n”,c); } int max(x,y) int x,y; { int z; z=(x>y)?x:y; return(z); } return语句执行图示: 主调函数 被调函数
函数返回值的注意事项 • 返回值类型要与函数类型一致,且以函数类型为准。 • 函数类型缺省时,系统自动按整型处理。 • 例如: int max(int x,int y ) { return x*y;}
补充说明 • 一个函数中可以有若干个return语句,流程执行到哪里,就从哪里返回主调函数。 • 若函数体内没有return语句,则执行函数到末尾,然后返回主调函数。 • 当不需要返回值的时候,可以用void定义函数为空类型,表示无返回值。
6.2 编写和调用自定义函数 • 函数调用的一般格式: 函数名(实参表); • 举例(设已建立max函数,为求最大值): …… main( ) { int x,y,z; scanf(“%d,%d”,&x,&y); z=max(x,y); …… }
函数调用中参数传递的两种方法 • 传值调用:函数调用中,采用赋值方式将实参的值传给形参。即单向调用。 • 传址调用:函数调用中,把实参的地址传给形参。在被调用函数中通过形参中的地址来访问调用函数中的实参。双向调用。
函数调用的三种方式 按照函数在程序中出现的位置来分; • 作为表达式出现在任何允许表达式出现的地方,参与运算。 如:a=sqrt(b); • 作为一条独立的语句完成特定的操作。 如:gets(string1); • 作为函数的参数被其他函数调用。 如:printf(“%s\n”,strcpy(str1,”Red rose”));
调用函数与被调函数的相对位置关系 • 如使用库函数,一般应在文件开头用: #include <……> …… • 函数调用遵循“先定义后调用”的原则。一般被调函数放在调用函数之前定义。 • 若被调函数在调用函数之后定义,就必须在调用函数中对被调函数加以如下说明: 类型名 被调函数名( );
例:被调函数在调用函数之后出现: main( ) { float sum( ); /*对被调函数的说明*/ float a,b,c; scanf("%f%f",&a,&b); c=sum(a,b); printf("sum=%f\n",c); } float sum(x,y) /*对函数sum进行定义*/ float x,y; { float z; z=x+y; return z; }
数组与函数 函数间传递数组数据的两种方法: • 值传递:将数组中的每个元素都作为一个参数来传递。 • 地址传递:通过地址传送方式将数组的首地址(如数组名)作为参数进行传递 。
数组元素作函数实参 • 数组元素(同简单变量)作函数实参,单向值传递,即“传值调用”方式。 • 【例6.17】用程序求出a数组中所有素数的和及平均值,函数isprime用来判断自变量是否素数。
int isprime(int x) /*该函数判断是否素数*/ { int i; for (i=2;i<=x/2;i++) if (x%i==0) return (0); return (1); } main( ) { int i,a[10],sum=0,n=0; float aver; printf("Enter 10 numbers:\n"); for(i=0;i<10;i++)
scanf("%d",&a[i]); printf("\n"); for(i=0;i<10;i++) if(isprime(a[i])==1) /*若a[i]素数*/ { printf("%d ",a[i]); sum+=a[i]; /*求素数和*/ n++; /*统计素数个数*/ } aver=sum/n; /*求素数平均值*/ printf("\nsum=%d,aver=%f\n",sum,aver); }
数组名作函数实参 • 形参和实参都采用数组名,传递是实参数组的起始地址,是地址传递方式。 • 【例6.18】求某学生10门课程成绩的平均分,课程成绩用数组存放。
例: float average(b) float b[10]; { int i; float aver,sum=0; for (i=0;i<10;i++) sum+=b[i]; aver=sum/10; return(aver); } 数组名作形参
main( ) { float a[10],ave; int i; printf("Input 10 scores:\n"); for (i=0;i<10;i++) scanf("%f",&a[i]); printf("\n"); ave=average(a); printf("average score is %5.2f",ave); } 数组名作实参,传递的是实参数组的起始地址
使用数组名作函数实参说明 • 数组名表示数组元素存放的起始地址,可认为是地址常量。 • 数组名作实参,传递是数组地址,而非数组元素。 • 字符串常量作实参,传递的字符串常量的首地址而非字符内容。 • 用数组名作函数参数,必须在主调函数和被调函数中分别定义数组,并保证类型一致。
6.3函数的嵌套调用 • C语言程序由函数构成。 • 函数与函数之间是平行的,独立的。 • 不允许在一个函数内部嵌套定义另一个函数;但允许在调用一个函数的过程中,又调用另一个函数。
函数a 函数b main函数 调用函数b 调用函数a 结束 函数的嵌套调用
6.4 编写递归函数 • 递归调用: 在调用一个函数的过程中又出现直接或间接的调用该函数本身,称为函数的递归调用。 注意: 递归结构构成了另外一种形如循环的结构。
递归函数设计举例:求n! • 算法描述: n!=1 × 2 × 3 × …… × (n-1)×n • 求积公式: 1 n=0 n ×(n-1)! n>0 f(n) =
5!的运算过程: ? 5! 120 5×4! 5×24 4×3! 4×6 3×2! 3×2 2×1 2×1! 1×0! 1×1 1
求n!递归源程序如下: long facto(n) int n; { long int z; if (n==0) z=1; else z=n*facto(n-1); return z; } main( ) { int x; long facto( ); printf("Input a number:\n"); scanf("%d",&x); printf("The result is %ld",facto(x)); }
6.4 局部变量和全局变量 基本概念: • 变量的生命期 变量占用存储空间的时限 • 变量的作用域 在变量占用存储空间的时间内是否能够被引用,即变量作用的有效范围是全局的还是局部的。
局部变量 • 局部变量 在一个函数内部定义的变量(内部变量),它只在此函数范围内有效,在此函数以外不能被使用。 • 局部变量的作用域所在函数内部
局部变量举例 a 、b、c有效范围 float f1(a) int a; { int b,c; …… } char f2(int x, int y) { int a,b; …… } main() { int m,n; …… } x、y、a 、b有效范围 m、n有效范围
局部变量使用说明: • 主函数main中定义的变量只在main函数中有效。 • 不同函数中可以使用同名变量,它们互不干扰。 • 形参也是局部变量。 • 在一个函数内部,可以在复合语句中定义变量,这些变量只在该复合语句中有效。这种复合语句称为“分程序”和“程序块”。
全局变量 • 在函数之外定义的变量是外部变量,外部变量是全局变量。全局变量可以为本文件中所有函数共用。 • 全局变量的有效范围为从定义变量的位置开始直到本源文件结束。 • 设置全局变量的作用是增加函数间数据联系的渠道。
全局变量举例 int i,j; /*全局变量*/ float f1(a) int a; { int b,c; …… } float p,q; /*全局变量*/ char f2(int x, int y) { int a,b; …… } main() { int m,n; …… } 全局 变量 i、j 的作用 范围 p、q 的作用 范围
全局变量的使用说明 在程序设计中,应尽量避免使用全局变量。 • 全局变量在程序的全部执行过程中都占用存储单元,而不是仅在需要时才开辟单元。 • 如果在同一个源文件中,外部变量与局部变量同名,则在局部变量的作用域范围内外部变量不起作用。 • 使函数的可靠性和通用性降低。 • 使用全局变量过多,会降低程序的清晰性。