940 likes | 1.08k Views
第八章 函数. 学习目标. 掌握函数的定义和调用 掌握实参和形参的关系 掌握内部变量和外部变量的概念 掌握动态变量和静态变量的概念. 结构化程序设计. 结构化程序设计. 什么是结构化程序设计? 自上而下逐步细化的模块化程序设计方法 结构化程序设计的好处是什么? 复杂问题简单化 便于多人协作完成一个大的任务 提高程序可维护性、可读性 模块复用 C 语言中如何实现结构化程序设计? 模块化在 C 语言中用函数实现. 函数基础. 问题提出. 我们在完成一项工作的时候往往需要由几个人,几个部门甚至几个单位共同来完成。
E N D
学习目标 • 掌握函数的定义和调用 • 掌握实参和形参的关系 • 掌握内部变量和外部变量的概念 • 掌握动态变量和静态变量的概念
结构化程序设计 • 什么是结构化程序设计? 自上而下逐步细化的模块化程序设计方法 • 结构化程序设计的好处是什么? 复杂问题简单化 便于多人协作完成一个大的任务 提高程序可维护性、可读性 模块复用 • C语言中如何实现结构化程序设计? 模块化在C语言中用函数实现
问题提出 • 我们在完成一项工作的时候往往需要由几个人,几个部门甚至几个单位共同来完成。 • 编写一个应用程序也是这样,可以将整个程序分解若干个程序模块,每个程序模块完成一个特定的功能,每个人负责一个或者几个程序模块,分别编写完成。 • c程序库已经提供了一些常用的函数,但是这远远不够,我们需要根据实际编程的要求自定义一些函数,通过自定义函数实现所要求的程序模块功能。
函数实例 #include <stdio.h> #include <math.h> main() //主函数 { int iNo,b; printf(“input a number:\n”);//输出函数 scanf(“%d”,&iNo); //输入函数 b=sqrt(iNo); //求平方根函数 printf(“%d”,b); //输出函数 }
函数实例 /*从键盘输入3个数,求和并输出。*/ #include <math.h> #include <stdio.h> int myAdd(int x,int y,int z) { int s; s=x+y+z; return s; } main() { int iNo1,iNo2,iNo3,sum; printf("input 3 numbers:\n");//输出函数 scanf("%d%d%d",&iNo1,&iNo2,&iNo3); //输入函数 sum=myAdd(iNo1,iNo2,iNo3); //用户自定义函数 printf("sum=%d",sum); //输出函数 }
什么是函数? 函数: 一段函数就是一段实现一定功能的代码,就像一台具有某种功能机器。 因此函数的调用就类似启动一台机器来完成某个任务。 C语言中,函数是实现模块化的工具。
问题提出:使用函数解决 输入若干个学生的学号、某门课程的平时成绩和期末成绩,计算出总评成绩。其中, 总评成绩 = 50%*平时成绩+50%*期末成绩 最后,从键盘输入一个学生的学号,查询该生的平时成绩、期末成绩和总评成绩。
问题分析:函数解题思路 • 定义若干具有不同功能的函数,在main函数中分别调用它们,用以解决这个问题。如: • main() • { • /*定义必要的变量*/ • inputScore(); /*输入学生学号及成绩信息* / • computeTotalScore();/*计算每个学生的总成绩*/ • searchByNo(); /*按学号查找某个学生的信息*/ • printSearchInfo();/*输出查找到的学生信息*/ • } main函数只负责解决问题的流程;具体子问题的实现交给不同的函数去处理。
函数体 函数的定义 除了系统提供的函数外,我们都需要对函数进行定义。 函数定义的通用形式为: 返回值数据类型 函数名称([参数数据类型 参数1 ] ,[ 参数数据类型 参数2, …]) { … } 函数首部
函数定义案例1 无参数无返回值的函数 如:定义printStar函数,用以在屏幕上输出5个*符号。 void printStar( ) { int star; for(star=1; star<=5; star++) printf("*"); printf("\n") ; } 函数无返回值时使用的数据类型
函数的调用 • 无参的调用形式: • 函数名();
无参数无返回值的函数的调用 #include <stdio.h> main( ) { printStar( );//函数调用 } void printStar( ) { int star; for(star=1; star<=5; star++) printf("*"); printf("\n") ; }
自定义函数的声明 在程序的开头加上对应函数的声明,就不会出现函数未 定义(或重复定义)的错误。 函数声明的形式: 返回值类型 函数名称([参数类型 参数1, …]) ; 其中前面的与函数定义时的首部完全一样,但末尾 必须加“;”。 如对printStar函数的声明为: void printStar( );
无参数无返回值的函数的声明 #include <stdio.h> printStar( ); /*函数声明*/ main( ) { printStar( ); /*函数调用*/ } void printStar( ) /*函数定义(首部)*/ { int star; for(star=1; star<=5; star++) printf("*"); printf("\n") ; }
问题提出 • 过年回家,车票难买,到处托人。 • 假如你先把车票钱给帮你买车票的朋友, 那么返回给你的应该是一张车票(当然要顺利的话)。 • 同理,在C语言中,函数的调用有时候也是有返回值的,那么如何理解?
问题分析 • 对应到函数上,你的朋友就是函数,车票钱是函数输入,车票就是函数的返回值,而你就是调用函数的主程序。
函数的返回值 • 函数的返回值 • 返回语句 • 形式: return(表达式); • 或 return 表达式; • 或 return; • 功能:使程序控制从被调用函数返回到调用函数中,同时把返值带给调用函数
函数定义案例2 无参数有返回值的函数 如定义函数sum_100,用以求1+2+3+…+100的和,并将结果返回给调用它的函数。 int sum_100( ) { int s=0, i; for(i=1; i<=100; i++) s=s+i; return s;/*用return返回一个值给主调函数* / }
无参数有返回值的函数案例 #include <stdio.h> int sum_100( ); /*函数声明*/ main( ) { int s1; s1 = sum_100( ); /*函数调用*/ printf("和值为:%d\n", s1); } int sum_100( ) /*函数定义(首部)*/ { int s=0, i; for(i=1; i<=100; i++) s=s+i; return s; /*返回值给主调函数*/ }
模仿练习 • 无参无返回值函数编写: 1、自定义函数myPrint1(),在屏幕上输出如下图形(每行10个$符号),并在主函数中调用myPrint1()函数。 $$$$$$$$$$ $$$$$$$$$$ $$$$$$$$$$
模仿练习 • 无参无返回值函数编写: 2、自定义函数myPrint2(),在屏幕上输出如下图形,并在主函数中调用myPrint2()函数。 @ @@@ @@@@@ @@@@@@@ @@@@@@@@@
模仿练习 • 无参有返回值函数编写: 3、自定义函数myFac(),计算10!,将计算结果返回给调用者。然后在主函数中调用myFac()函数,输出该结果。 4、自定义函数myMax(),实现功能:从键盘输入3个数,求最大数并在主函数中调用myMax()函数,输出该结果。
讨论 1、为什么使用函数? 2、如何理解函数调用与函数定义的不同? 3、空函数在程序结构设计中有什么作用?
学以致用 将自己的“学生成绩管理系统”(循环菜单)以模块化的形式进行处理,具体实施方案: • 主菜单的显示和选择部分可定义函数menu_select()来调用, • “编辑子菜单”可定义editmenu()来调用, • “查找”可定义serachmenu()来调用。
函数定义案例3 有参数无返回值的函数 如定义printStar_n()函数,用以在屏幕的一行上输出n个*号。其中n的值是由调用该函数的函数传递过来的。 void printStar_n( int n) { int star; for(star=1; star<=n; star++) printf("*"); printf("\n"); } 函数参数(称为形参)
函数调用 有参函数的调用形式: 函数名(实参); 实参与形参: • 个数相等 • 类型一致 • 按顺序一一对应
调用时传递的参数(称为实参) 函数参数(称为形参) 有参数无返回值的函数调用 #include <stdio.h> void printStar2(int n); //函数声明 main( ) { int starNum; scanf("%d", &starNum); printStar_n(starNum); //函数调用 } void printStar_n( int n) //函数定义(首部) { int star; for(star=1; star<=n; star++) printf("*"); printf("\n"); }
函数参数(称为形参) 函数定义案例4 有参数有返回值的函数 如定义函数sum_n(),用以求1+2+3+…+n的和,并将结果返回给调用它的函数。 int sum_n( int n ) { int s=0, i; for(i=1; i<=n; i++) s=s+i; return s; /*通过return返回一个值*/ }
有参数有返回值的函数调用 #include <stdio.h> int sum_n( int n ); /*函数声明*/ main( ) { int s1, n; scanf("%d", &n); s1 = sum_n(n );/*函数调用*/ printf("和值为:%d\n", s1); } int sum_n( n ) /*函数定义 (首部) */ { int s=0, i; for(i=1; i<=n; i++) s=s+i; return s; /*返回一个值给主调函数*/ }
函数的调用 1、语句调用方式 2、函数表达式 3、函数参数
函数的调用 调用方式 • 语句调用: 例 printmsg(); printf(“打印成功!\n”); • 函数表达式调用: 例 result=aver(totalscore, stuno); • 函数参数调用: 例 printf(“%f”,aver(totalscore, stuno));
模拟练习 • 有参无返回值函数编写: • 1、自定义函数myprint_n(),在屏幕的一行上输出n个$符号。在主函数中调用myprint_n()函数,输出如下图形(每行上的符号个数由调用它的函数传递过来) 。 • 函数首部为:void myprint_n( int n) • $$$$$(5个$) • $$$$$$$$$$(10个) • $$$$$$$$$$$$$$$(15个) • $$$$$$$$$$$$$$$$$$$$(20个) • 2、修改以上程序,实现以下功能:打印个数由用户输入决定,支持用户循环输入,当用户输入0时结束程序。
讨论 1、函数的功能应该尽量保持相对独立还是依 赖函数外的数据为好 ? 2、怎样使一个函数具有更大的通用性? 3、函数参数的值传递有哪些特点?
扩展练习 将“控制流程”阶段完成的“小型计算器”功能改写为函数,并在主函数中调用,注意参数的类型定义及传参过程。
函数的值传递方式 #include <stdio.h> void main() { void swap(int a,int b); /*swap函数的声明*/ int x=7,y=11; printf("x=%d,\ty=%d\n",x,y); printf("swapped:\n"); swap(x,y); /*调用swap函数*/ printf("x=%d,\ty=%d\n",x,y); } void swap(int a,int b) /*定义swap函数*/ { int temp; temp=a; a=b; b=temp; } 输出结果:x=7, y=11 swapped: x=7, y=11
结论 值传递方式 • 方式: • 函数调用时,为形参分配单元,并将实参的值复制到形参中; • 调用结束,形参单元被释放,实参单元仍保留并维持原值 • 特点: • 形参与实参占用不同的内存单元 • 单向传递
结论 • 函数参数的传递方式还有另一种---地址传递方式 • 其传递的参数为地址而不是值,具体内容将在数组章节中学习.
问题提出 #include <stdio.h> void prt(); main() { int x; for(x=1; x<=5; x++) prt(); } void prt() { static int y=0; y++; printf("%d", y); } 输出结果:12345 去掉static后的 结果为多少?
问题分析 • 动态存储变量和静态存储变量 • 动态变量:用auto关键字表示 • 如auto int p, f; //p,f为两个动态变量 • 在C中,默认的变量定义就是定义为动态变量 • 即上述的p,f变量定义与 int p, f; 是完全等价的。 • 静态变量:在程序生存期内一直有效。 • 注意,如果静态变量出现在函数中,不会因为函数的调用结束而丢失这个变量的值
变量的生存期 内部变量与外部变量的作用区域是不一样的。 int a,b,c; //外部变量定义 main() { int x, y; //内部变量 float f; //内部变量 //其他语句… } int sum(int n) { int s=0, i; //内部变量 //语句 } x, y, f的 有效区域 a, b, c的 有效区域 s,i的 有效区域
小结 1、变量的存储类型 计算机的存储器分为内存和外存。还有一个小小的临时存储器称为寄存器,用以存储一些反复被加工的数据。 C语言允许程序员区分是在主存还是在寄存器中开辟变量的存储空间。 2、变量的生存期 (1)在编译时分配存储单元。这种变量的生存期为程序执行的整个过程,在该过程中占有固定的存储空间,称为永久存储。 (2)只在程序执行的某一段时间内存在。比如在函数的执行过程中存在。这种存储方式称为动态存储。
3、变量的可用域 变量的可用域也分为全局可用和局部可用。 C语言中,用“存储属性”来表示以上三个方面的属 性,如表所示。 小结
模仿练习 1、写出以下程序的运行结果: intfun(int a) /*a为形参,自动型局部变量*/ { auto int b=0; /*b是自动型局部变量*/ static int c=3; /*c是静态型局部变量,初始化仅进行一次*/ b+=1; c=c+1; return a+b+c; } main() { int a=2,i; /*a和i都是自动型局部变量*/ for(i=0;i<3;i++) printf("%d\t",fun(a)); }
模仿练习 2、写出以下程序的运行结果: int sum(int n); //函数声明 int a=4,b=5,c=6; //外部变量定义 main() { int x=1, y=2; float f=3.0; x=sum(10); printf("x=%d,y=%d,f=%f\n", x, y, f); printf("a=%d,b=%d,c=%d\n", a, b, c); //printf("s=%d, i=%d\n", s, i); } int sum(int n) { int s=0, i; for(i=1; i<=n; i++) s = s+i; printf("s=%d, i=%d\n", s, i); printf("a=%d,b=%d,c=%d\n", a, b, c); //printf("x=%d,y=%d,f=%f\n", x, y, f); return s; }