710 likes | 823 Views
第 三 章. 模块化程序设计. 引出. 如果程序中有多个数,多处需要判断是否是素数,怎么办?反复书写那段重复的代码? 解决办法: 将判断素数的功能用一个独立的模块来实现,即自定义函数;在 main 函数中反复调用此功能模块就可以了。. for(i=2;i<=x/2;i++) if(x%i==0) break; if(i>=x/2) printf("yes<br>"); else printf("no<br>");. void sushu(int x) {int i; for(i=2;i<=x/2;i++)
E N D
第 三 章 模块化程序设计
引出 • 如果程序中有多个数,多处需要判断是否是素数,怎么办?反复书写那段重复的代码? • 解决办法:将判断素数的功能用一个独立的模块来实现,即自定义函数;在main函数中反复调用此功能模块就可以了。 for(i=2;i<=x/2;i++) if(x%i==0) break; if(i>=x/2) printf("yes\n"); else printf("no\n");
void sushu(int x) {int i; for(i=2;i<=x/2;i++) if(x%i==0) break; if(i>=x/2) printf("yes\n"); else printf("no\n"); } • 模块化的优点: • 实现了代码的复用; • 编程效率高;代码少; • 功能分解,易于实现; • 便于合作开发大型项目等。 main() { int a,b; printf("input a number:"); scanf("%d%d",&a,&b); sushu(a); sushu(b); }
[教学要求] 1.掌握函数定义的一般形式。 2.掌握函数调用的两种方式。 3.理解函数形参与实参的对应关系、参数传递方法及函数返回值的概念。 4.掌握指针的含义。 5.掌握指针作为函数参数的数据传递特点。
函数定义的基本知识 • 函数的定义位置:应写在其它函数的外面,前后都可以,但不能写在内部。 • 函数的定义形式: • 第一行写函数名和括号 • 第二行开始是函数体,用大括号扩起来,如: 函数名称用户自定
函数定义的声明:函数被调用在前面,而函数定义书写在后面的,需要在使用该函数前进行声明。函数定义的声明:函数被调用在前面,而函数定义书写在后面的,需要在使用该函数前进行声明。 • 函数声明的形式:将函数定义的第一行信息重新写一遍,末尾加分号。 声明可以放在调用之前的任意位置处。 void sushu(int x) ; main() { …… sushu(a); …… } void sushu(int x) {…… }
函数声明的几种变通形式: main() { float add(float x,float y); float a,b,c; scanf("%f,%f",&a,&b); c=add(a,b); printf("sum is %f",c); } float add(float x, float y) { float z; z=x+y; return(z); } float add(float ,float ); float add(float a,float b);
函数定义的高级知识 • 函数的参数 • 函数的返回值
主调函数 自定义函数 参数 返回值 int sushu(int x)
有参函数 无参函数 函数的形式分类1 无论有参、无参,函数名后的括号不能省略。 函数的形式分类2 • 有返回值函数 • 无返回值函数
通常也无返回值,仅需书写: void 函数名(); 例如: void clrscr(); void draw() { …… } main() { draw(); draw(); } 如果有返回值,则写明返回值类型, 例如 char getchar(); ch=getchar(); 无参函数定义 特点:空括号
需写明参数的个数和类型: 类型函数名(类型 参数1,类型 参数2,…) 如:int sushu(int n); float fun(int m, int n); 【强调】:形参定义时需分别写明它们的类型,类型相同不能只写一个,例如下错误: float fun(int m, n); 有参函数定义 × 例如:求两个数的最大值的函数参数设计: max( ) float float x, float y
分析设计函数首部信息: 实验4-1-4: float fun(int n) 实验4-1-5: float fun(int m,int n) 实验4-1-7: void fun() 函数定义分析练习
有返回值函数,在函数体内用return语句返回处理的结果值。有返回值函数,在函数体内用return语句返回处理的结果值。 如:int sushu(int x) { int i; for(i=2;i<=x/2;i++) if(x%i==0) return 0; return 1; } return语句的两个功能:返回值、结束函数调用。 有返回值函数定义
如果函数不返回值,则名称前写void,如: void draw(int x,int y) 如果定义函数时名称前没写明类型,默认为int类型,如: draw(int x,int y) 【注意】:如果函数体内return语句返回值的类型与定义的函数名前类型不同,则以函数名前面的类型为准,系统会自动进行类型转换,可能会产生误差或错误。
例:编写一个函数,其功能是求m的n次方,即mn。例:编写一个函数,其功能是求m的n次方,即mn。 函数定义练习 ? • 分析:函数参数 • 函数返回值 2个,int类型参数 返回值为float或double类型 函数首部信息如下: double fun(int m,int n)
double fun(int m,int n) { int i; double t=1; for(i=1;i<=n;i++) t=t*m; return t; } 形参在括号中定义后,直接在函数中使用,不能再次定义。 return (表达式); return 表达式; main() { int a,b; double c; printf("input two numbers:"); scanf("%d%d",&a,&b); c=fun(a,b); printf("%.0lf\n",c); }
形参和实参 形参:定义时括号内的参数叫做形式参数,简称形参。 实参:调用是给予的实际数值叫做实际参数,简称实参。 形参和实参:个数相同、类型一致、一一对应。 例如求m的n次方的函数: float fun(int m,int n) 调用时如果求4的5次方,只能书写fun(4,5);不能写为fun(5,4);
double fun(int m,int n) { int i; double t=1; for(i=1;i<=n;i++) t=t*m; return t; } 有返回值函数调用 实参可以是常量、变量、表达式等。如:fun(2,8) fun(3,1+5) main() { int a,b; double c; printf("input two numbers:"); scanf("%d%d",&a,&b); c=fun(a,b); printf("%.0lf\n",c); } 赋 值
double fun(int m,int n) { int i; double t=1; for(i=1;i<=n;i++) t=t*m; return t; } 有返回值函数调用 main() { int a,b; printf("input two numbers:"); scanf("%d%d",&a,&b); printf("%.0lf\n", fun(a,b)); } 输 出
double jiecheng(int n) { int i; double t=1; for(i=1;i<=n;i++) t=t*i; return t;} 有返回值函数调用 main() { int a,i; double b=0; scanf("%d",&a); for(i=1;i<=a;i++) b=b+jiecheng(i); printf("%.0lf\n",b); } 表达式运算
有返回值函数调用 b=jiecheng(a); b=b+jiecheng(i); printf("%.0lf\n", jiecheng(a)); • 总结: • 将函数调用看做一个可以利用的值。 • 函数调用一般不作为独立语句存在。 • jiecheng(a); ×
无返回值函数调用 编写一个函数,其功能画m行n列的矩形。 ? 2个,int类型参数 无返回值,为void • 分析:函数参数 • 函数返回值 函数首部信息如下: voiddraw(int m,int n)
void draw(int m,int n) { int i,j; for(i=1;i<=m;i++) { for(j=1;i<=n;i++) printf(“*”); printf(“\n”); } } 总结: 函数调用作为独立语句存在。 void main( ) { int x,y; scanf(“%d%d”,&x,&y); draw(x,y); }
函数调用总结: • 两种形式: • 有返回值函数调用看做一个可以利用的值。 • 无返回值函数调用作为独立语句存在。
本次课内容小节 • 函数定义的基本形式 • 函数定义的参数表达 • 函数定义的返回值类型 • 函数调用的实参表达 • 函数调用的2种形式
编程练习 • 编写一个函数,判断一个给定的数是否是水仙花数。 • 编写一个函数,功能是输出如下三角形图案。
编写函数,求三角形面积。 函数参数:三角形的三条边 函数返回值:三角形面积。 函数编程练习 3-3
编写函数判断一个数是否是完数。 完数:一个数如果恰好等于它的因子之和,这个数就是完数,例如6的因子为1、2、3,而6=1+2+3,因此6是完数。 要求函数返回整型值。 编程练习(实验题目) 3-4
3.7 库函数的使用 参考书后附录,P303 注意包含相应的头文件。 注意理解参数的类型和含义。
3.8 局部变量与全局变量
例 不同函数中同名变量 局部变量---内部变量 在函数内定义的变量是局部变量, 其作用域:只在本函数内有效 声明: 不同函数中可定义的局部变量可同名,占不同内存单元 形参属于局部变量 在复合语句中也可以定义它的局部变量,其作用域是只在此复合语句中有效。 float f1(int a) { int b,c; ……. } char f2(int x,int y) { int i,j; …… } main() { int m,n; ……. {int i;…… } } main() { int a,b; a=3; b=4; printf("main:a=%d,b=%d\n",a,b); sub(); printf("main:a=%d,b=%d\n",a,b); } sub() { int a,b; a=6; b=7; printf("sub:a=%d,b=%d\n",a,b); } a,b,c有效 x,y,i,j有效 m,n有效 i有效
全局变量---外部变量 函数外部定义的变量称为全局变量 其作用域为定义位置后的任何函数 int a=13,b=-8; int max() { int z; z=a>b?a:b; return(z); } main() { printf(“a=%d,b=%d",a,b); printf("max=%d",max()); }
例 全局变量举例 int max(int x, int y) { int z; z=x>y?x:y; return(z); } main() { printf(“a=%d,b=%d",a,b); printf("max=%d",max(a,b)); } int a=13,b=-8; 因为全局变量的作用域为定义位置后的任何函数 编译出错
全局变量意味着多个函数可使用它,就是说各函数之间通过全局变量产生了相互的值的影响,因此降低了函数的独立性,使各函数间依赖型增强。 应尽量少使用全局变量
int a=13,b=8; int sum() { int z; z=a+b; a++; b--; return(z); } main() { printf(“a=%d,b=%d",a,b); printf(“sum=%d",sum()); printf(“a=%d,b=%d",a,b); } 输出结果为: a=13,b=8; sum=21; a=14,b=7;
例 全局变量与局部变量同名举例 int a=3,b=5; max(int a, int b) { int c; c=a>b?a:b; return(c); } main() { int a=8; printf("max=%d",max(a,b)); }
例 全局变量副作用 int i; main() { void prt(); for(i=0;i<5;i++) prt(); } void prt() { for(i=0;i<5;i++) printf(“%c”,’*’); printf(“\n”); } 运行结果:*****
动态存储变量和静态存储变量 局部变量的存储类别:动态存储变量和静态存储变量。 auto动态存储变量是在程序运行时根据需要动态分配存储空间。函数被调用时,分配给内存单元,调用结束后就释放内存单元。 static静态存储变量局是指程序运行期间分配固定的存储空间。每次函数调用结束后不释放空间,只有在主程序运行结束才释放内存单元。 static静态变量在函数多次调用时,只赋一次初值,每次调用后保留当时的结果值。
变量的声明类型 局部变量的定义类型 auto自动类型 如:atuo int i,a,b; static静态类型 如:static int x,y; register寄存器类型 如:register int i; 补充:如无特殊声明,默认为auto类型 • 全局变量的定义类型 • static静态类型(默认是static类型) • extern 外部类型 如:extern int s; • 补充:如无特殊声明,默认为static类型
a=0,b=-10,n=1 a=4,b=10,n=13 a=0,b=-10,n=13 a=6,b=10,n=25 int n=1; void func() { static int a=2; int b=5; a+=2; b+=5; n+=12; printf("a=%d,b=%d,n=%d\n",a,b,n); } main() { int a=0,b=-10; printf("a=%d,b=%d,n=%d\n",a,b,n); func(); printf("a=%d,b=%d,n=%d\n",a,b,n); func(); }
3.9 模块化程序设计 —指 针
普通函数参数值传递的特点 单向、值传递 函数return语句返回值的特点 只能返回一个值
void swap(int x,int y) { int t; t=x; x=y; y=t; } main() { int a,b; printf("input a number:"); scanf("%d%d",&a,&b); swap(a,b); printf("After changer a=%d,b=%d\n",a,b); } 输入4,3 输出after change:a=4, b=3 本例说明:自定义函数中修改了形参变量的值,反过来不会影响主调函数中实参的值。
问题的引入 1、能不能在自定义函数中通过修改形参而改变主调函数中的变量值。 2、如果想同时返回多个处理值给主调函数怎么办? 例题1:编写函数分别返回双精度实数的整数部分和小数部分。 例题2:编写函数交换两个变量的值,并在主函数中输出结果。
解 决 方 案-使用指针类型的参数 void fun(int *x,int *y) { int t; t=*x; *x=*y; *y=t; } main() { int a,b; printf("input a, b:"); scanf("%lf%d",&a,&b); fun(&a,&b); printf("after call a=%d,b=%d\n",a,b); } 1、定义指针参数 2、间接访问 3、传递地址
解 决 方 案-使用指针类型的参数 1、定义指针参数 int fun(double a,double *b) { int x; x=(int)a; *b=a-x; return x; } main() { double x,y; int c; scanf("%lf",&a); c=fun(x,&y); printf(“zhengshu %d,xiaoshu %lf",c,y); } 2、间接访问 3、传递地址
通过演示的例题说明: 为什么有时需要在函数的参数中使用指针类型的参数? 1.可以同时将多个函数处理结果返回给主调函数。 2.可以间接访问主调函数的实参变量,即将函数内的值反向传递给实参变量。