470 likes | 642 Views
第七章 函 数. 7 -1 函数的定义与调用 7-2 外部函数和内部函数 7-3 内部变量和外部变量 7-4 变量的存储类型 7-5 函数的数据传递 7-6 数组作为函数参数 7-7 递归函数. 7. 1 函数的定义与调用. 一个源程序文件由一个或多个函数组成。 一个 C 程序由一个或多个源程序文件组成; C 程序执行从 main() 开始,在 main() 中结束; 所有 C 函数都是平行(独立)的,不能嵌套定义; 函数分类: 从用户角度: 标准函数和用户自定义函数; 从函数形式: 无参函数和有参函数;.
E N D
第七章 函 数 7-1 函数的定义与调用7-2 外部函数和内部函数7-3 内部变量和外部变量7-4 变量的存储类型7-5 函数的数据传递7-6 数组作为函数参数7-7 递归函数
7. 1 函数的定义与调用 • 一个源程序文件由一个或多个函数组成。 • 一个C程序由一个或多个源程序文件组成; • C程序执行从main()开始,在main()中结束; • 所有C函数都是平行(独立)的,不能嵌套定义; • 函数分类: • 从用户角度: 标准函数和用户自定义函数; • 从函数形式: 无参函数和有参函数;
7.1.1 函数定义 函数定义的一般形式: 函数类型 函数名(类型 形式参数1,类型 形式参数2,…) {//函数体 函数内部变量说明 函数执行语句 }
无无参函数 类型标识符函数名( ) {说明部分 语句 } • 空空函数 类型标识符函数名( ) { }
例:设计函数,求1+……+n的和 Int sum(int n) { int i,s=0; for (i=1;i<=n;i++) s=s+i; return s; }
一、类型标识符——函数的返回值类型 • 函数返回值指通过函数调用得到的一个确定的值; • 函数返回值通过return语句得到。形式为: return(表达式); 一个函数可以几个return语句。 • 函数返回值的类型为定义函数时说明的类型; • C规定:定义函数时不说明类型,默认为整型; • 函数值类型与return中表达式类型不一致时,以函数值类型为准; • 函数若无return语句,不是不带回值,而是带回不确定的值;可用“void”定义“无类型”,保证不使函数带回任何值。
#include <stdio.h> int abs(int x) { return x>0?x:-x; } main() { int n; scanf("%d",&n); printf("%d\n",abs(n)); }
#include <stdio.h> void abs(int x) { int y=(x>0?x:-x); printf("%d\n",y); } main() { int n; scanf("%d",&n); abs(n); }
以下函数的返回值: #include <stdio.h> max(float x,float y) { return x>y? x: y ; } main() { printf("%d\n",max(2,3.5)); } 程序运行结果: 3
二、函数参数 • 形式参数: 定义函数时,函数名后面括号中变量名; • 实际参数: 调用函数时,函数名后面括号中变量名;
三、函数的调用 • 一般形式: 函数名(实际参数表) • 说明: • 实参间用逗号隔开; • 实参与形参必须一一对应; • 函数调用方式 • 函数语句:printf(“%d,%d”,a,b); • 函数表达式:c=2*max(a,b) • 函数参数: m=max(max(a,b),c);
函数参数之间的数据传递 1.调用函数时: • 给形参分配存储单元; • 将实参的值传递给形参; • 执行函数体; • 返回函数值,释放形参占用存储单元。 2.说明: • 形参只有在函数调用时才分配存储单元,调用结束释放; • 实参可以是常量、变量、表达式; • 实参与形参类型应一致; • C规定,实参与形参间为“值传递”,即单向传递。
“值传递”举例 a 5 b 8 x 5 y 8 swap(int x,int y) {int t; t=x;x=y;y=t; printf(“x=%d,y=%d\n”,x,y); } main() {int a,b; a=5;b=8; swap(a,b); printf(“a=%d,b=%d”,a,b); } 调用前 调用开始时 调用结束后
#include <stdio.h> long fun(int x) { int i; long f=1; for (i=2;i<=x;i++) f=f*i; return f;} void main() { int m,n; long c; do { printf("m,n:"); scanf("%d%d",&m,&n); } while (m<=n); c=fun(m)/(fun(n)*fun(m-n)); printf("c=%ld\n",c); }
编写函数,验证陈景润研究的哥德巴赫猜想:任意大偶数为两个素数之和并输出这两个素数(所谓大偶数是指6开始的偶数)。编写函数,验证陈景润研究的哥德巴赫猜想:任意大偶数为两个素数之和并输出这两个素数(所谓大偶数是指6开始的偶数)。 • 要求: • prime(int x)实现判断一个数x是否为素数, 返回值为0 或1 ,0 表示不是素数,1 表示是素数 • main()实现输入一个大偶数,并输出该大偶数对应的两个素数。
include <stdio.h> #include <math.h> int prime(int x); /*函数声明*/ void main() { int n,i; for (n=1000;n<=10000;n+=2) { for (i=1;i<=n/2;i++) if (prime(i)==1 && prime(n-i)==1) { printf("%d=%d+%d\n",n,i,n-i); break; } } } int prime(int x) { int i; for (i=2;i<(int)sqrt(x);i++) if (x%i==0) return 0; return 1;}
被调函数需具备的条件 • 被调函数函数必须存在(自定义或库函数); • 若被调函数是库函数,须在文件头加 #include “~”; • 若被调函数为自定义函数, 且与主调函数在同一文件中,一般应在主调函数中对被调函数作声明, 形式为: 类型名被调函数名( 类型1 参数1, 类型2 参数2, …) 或 类型名被调函数名( 类型1, 类型2, …) 函数原型
C规定,以下几种情况可不作被调函数声明: • 被调函数的定义出现在主调函数之前; • 已经在所有函数定义之前作了函数声明;
练习 • 求三个数的最小公倍数。 • 求x的y次方
函数的嵌套调用 void main() {printf(“sum=%d\n”,total(5)); } int fac(int n) {int i,s=1; for(i=1;i<=n;i++) s=s*i; return(s); } int total(int n) {int i,s=0; for(i=1;i<=n;i++) s=s+fac(n); return(s); } • 所谓嵌套调用,就是在调用一个函数的过程中,又调用另一个函数。即: 函数A中调用函数B,函数B中又调用函数C。例如:
练习: 若有以下程序 int f(int x, int y) { return (y-x)*x ; } void main() { int a=3, b=4, c=5, d; d=f( f(3, 4), f(3, 5) ) ; printf(“%:d); } 执行后输出结果是 ______ 有以下程序 char fun (char x, char y) { if (x<y) return x; return y; } void main() { int a='9', b='8', c='7'; printf(“%c” fun(fun(a, b), fun(b, c)); } 程序的执行结果是 ---------
有以下程序 void f(int x, int y) { int t; if (x<y) { t=x; x=y; y=t; } } void main() { int a=4, b=3, c=5; f(a, b); f(a, c); f(b, c); printf(“%d,%d,%d”,a,b,c } 运行结果是______________
以下程序的功能是调用函数fun计算: m=1-2+3-4…+9-10,并输出结果,请填空 。 int fun( int n ) { int m=0, f=1, i; for (i=1; i<=n; i++) { m+=i*f; f= _________ ;} return m;} main() { printf(“%d”,_________); }
请在以下程序第一行的下划线处填写适当内容,使程序能正确运行。 _________(double, double) ; void main() { double x, y; scanf(“%ld%ld”, x, y) ; printf(“%____”,max(x, y)); } double max(double a, double b) { return (a>b?a:b) ; }
以下sum函数的功能是计算下列级数之和。 S=1 + x + x2/2! + x3/3! + … xn/n! 请给函数中的各变量正确赋初值。 double sum(doudle x, int n) { int i; double a, b, s; ________________ for (i=1; i<=n; i++) { a=a*x; b=b*i; s=s+a/b; } return s; }
7.2 外部函数和内部函数 1. 外部函数 在源文件中定义的函数,可以被同一源程序的其他源文件调用,则称为外部函数。 extern 数据类型 函数名(形参定义表) { } //extern 可以省略 2. 内部函数 在源文件中定义的函数,只能在该文件内使用,则称为内部函数。 static 数据类型 函数名(形参定义表) { }
7.3 内部变量和外部变量 • 内部变量(局部变量) 在一个函数内部定义的变量。只在本函数内部有效。 • 说明: • 主函数main中定义的变量也只在main中有效; • 不同函数中定义的变量名可以同名,互不干扰; • 形式参数也是局部变量; • 可在一组复合语句中定义变量,该变量只在本复合语句中有效(分程序);
#include <stdio.h> void main() { int n=2; { int n=10; printf("n=%d\n",n); } printf("n=%d\n",n); }
外部变量(全局变量) 在函数外部定义的变量。有效范围为从定义位置开始到 本源文件结束。 • 外部变量的作用:增加函数间联系的渠道。 • 外部变量的局限: • 占用存储单元; • 降低函数的通用性; • 降低函数的清晰性。 • 当外部变量与局部变量同名时: 在函数内部只有局部变量起作用。
#include <stdio.h> void fun(); int n=2; /*外部变量定义*/ main() { int n=10; fun(); printf("n=%d\n",n); } void fun() { printf("n=%d\n",n); }
#include <stdio.h> void fun(); main() { extern int n; /*外部变量声明*/ fun(); printf("n=%d\n",n); } int n; /*外部变量定义*/ void fun() { n=10; }
7.4 变量存储类别 • 变量分类: • 从变量作用域分:全局变量和局部变量; • 从变量生存期分:静态存储变量和动态存储变量; • 变量存储方式: • 静态存储方式:指在程序运行期间分配固定存储空间的方式; • 动态存储方式:指在程序运行期间根据需要进行动态分配存储空间的方式;
Auto变量 • 格式: auto 类型 变量名; • 该变量在动态存储区动态的分配空间; • 局部变量和函数形式参数都属auto变量; • “auto”可以省略。如在函数内部定义的: auto int a,b; 等价于: int a,b; 这样变量的值在函数调用结束后自动释放。 • 如果希望变量的值在函数调用结束后仍保留,可通过指定该局部 变量为“局部静态变量”实现。
Static局部静态变量 • 格式:static 类型 变量名; • 说明: • 局部静态变量在静态存储区内分配存储单元,在整个程序运行期间都不释放; • 局部静态变量赋初值在编译时只进行一次(例8-17); • 静态变量不赋初值自动赋0; • 局部静态变量在调用函数结束后仍然存在,但其他函数不能引用它(P175例8-18); • 静态存储缺点: • 占用存储空间; • 降低程序可读性。
7.6数组作为函数参数 A[i] 5 B[I] 8 x y • 数组元素作函数实参; 同变量实参,为单向“值传递” 例:
例7.19 统计成绩数组中不及格人数 #include <stdio.h> #define N 10 int fun(int x); void main() { int a[]={90,65,48,82,52,67,45,92,58,87},i,num=0; for (i=0;i<N;i++) num+=fun(a[i]); printf("num=%d\n",num); } int fun(int x) { if (x<60) return 1; else return 0; }
2. 数组名作函数参数(实参和形参均为数组名) • 要求在主调函数和被调函数中分别定义数组; • 实参数组和形参数组类型应一致; • 数组名作函数参数时,传递的时数组首地址;即实参数组和形参数组共用同一段内存单元,二者互相影响; • 形参数组可不定义大小; 实参score 形参array
例7.20 计算成绩数组的平均分 float fun (int b[],int n) { float s=0; int i; for (i=0;i<n;i++) s+=b[i]; return s/n; }
#include <stdio.h> #define N 10 float fun(int b[],int n); void main() { int a[]={90,65,48,82,52,67,45,92,58,87}; float avg; avg=fun(a,N); printf("avg=%g\n",avg); }
例7.21 分析程序结果 #include <stdio.h> #define N 10 void fun(int b[],int n) { int i,temp; for (i=0;i<n/2;i++) { temp=b[i]; b[i]=b[n-i-1]; b[n-i-1]=temp; } }
void main() { int a[N],i; for (i=0;i<N;i++) a[i]=2*i; for (i=0;i<N;i++) printf("%d ",a[i]); printf("\n"); fun(a,N); for (i=0;i<N;i++) printf("%d ",a[i]); printf("\n"); }
7.7 递归函数 • 递归调用指一个函数又直接或间接调用它本身。形如: • 直接递归:A->A • 间接递归:A->B->A • 年龄问题: • age(n)=age(n-1)+2 (n>1) • age(n)=10 (n=1) • 递归函数: int age(int n) {int c; if (n==1) c=10; else c=age(n-1)+2; return(c); }
递归函数 • age(5)=age(4)+2 • =age(3)+2 • =age(2)+2 • =age(1)+2 • =10+2 递推 回推 • 递归调用过程: • 回推 • 递推 • 递归函数的条件: • 递推公式 age(n)=age(n-1)+2 (n>1) • 结束条件 age(n)=10 (n=1)
例: 求n! • n!=1 (n=0或1) • n!=n*(n-1)! (n>1) long factl(long m) { if(m==1) return (1); else return(m * factl(m-1)); }
/*文件名:exam7_22.cpp*/ #include <stdio.h> int fun(int i) { if (i==1) return i; else return i*fun(i-1); } void main() { printf("%d\n",fun(4)); }
/*文件名:exam7_23.cpp*/ #include <stdio.h> int gcd(int x,int y) { if (y<=x && x%y==0) return y; else if (y>x) return gcd(y,x); else return gcd(y,x%y); } void main() { int a,b; printf("输入两个正整数:"); scanf("%d,%d",&a,&b); printf("最大公因子为:%d\n",gcd(a,b)); }
例8-9:Hanoi 塔问题。 移n 个盘子从A柱到C柱借助B柱。 • 可分解为以下三步: • 移n-1个盘子从A柱到B柱借助C柱 • 移1个盘子从A柱到C柱 • 移n-1个盘子从B柱到C柱借助A柱