1、第四章 函数,2019年7月30日星期二,练习,编写一个求阶乘的函数,利用该函数完成第四章课后习题第8题。 编写一个判断素数的函数(是素数返回1,否则返回0),利用该函数完成第四章课后习题第10题。,引例利用海伦公式计算任意三角形的面积,#include #include void main() float s,a,b,c,p;coutabc;p=(a+b+c)/2;s=sqrt(p*(p-a)*(p-b)*(p-c);cout“三角形的面积是:“sn; ,调用库函数,求平方根,函数是具有特定功能的相对独立的代码段。库函数是在C+编译系统中已经预先定义的函数。 用户可根据自己的需要将一段完成功
2、能相对独立的代码定义为一个函数,这类函数称为用户自定义函数。函数一经定义可被多次、反复调用。,函数的定义有参函数,int max(int x,int y) int z=xy?x:y;return (z); ,函数返回值类型, 若为int可省略,形式参数,必须分别列出每个参数的类型,结束函数的执行,返回到调用处,函数的返回值,必须与函数类型一致,形式参数是调用该函数时需要函数处理的数据。返回值是函数调用完后回应给调用者的结果。,函数名,函数的定义无参函数,void prn(void) cout“the max is”;,函数类型,无返回值时void不可省略,形式参数,无参数时可省略void,函数
3、名,return;,函数类型为void,所有无return语句,函数调用,函数的功能是通过在程序中对其调用来实现的。调用一个函数,就是把控制权转去执行该函数的函数体,函数体执行完之后,再将控制权转到调用函数处。 无参函数的调用格式一般为:函数名( ) 有参函数的调用格式一般为:函数名()中的参数称为实际参数或实参。,函数的调用(先定义,后调用),#include /函数定义 void main(void) int a,b,c;cinab;c=max(a,b);prn();coutcendl; ,执行过程:,Main函数,调用函数max,调用函数prn,max函数,函数体,prn函数,函数体,函
4、数调用的使用方式如下: (1) 对于有返回值的函数,调用出现在表达式中。 (2) 对于没有返回值的函数,函数调用只能通过函数调用语句实现。,函数调用方式: 赋值 如:c=max(a,b); 表达式中 c=1+max(x,y); coutmax(x,y); 执行函数 prn();,函数的调用参数的传递,void main(void) int a,b,c; cinab; c=max(a,b); /函数表达式 prn(); /函数语句 coutcendl;,实际参数,必须与形式参数个数相等,类型一致,按顺序对应!多于一个时以逗号分隔,实参可以为表达式,传值调用:实参传给形参(单向)。形参改变不会影响
5、实参,练习定义计算圆面积的函数,利用函数调用计算圆环面积,函数名,函数类型,(形参表),函数体return 语句,练习定义计算圆面积的函数,利用函数调用计算圆环面积,area,函数类型,(形参表),函数体return 语句,练习定义计算圆面积的函数,利用函数调用计算圆环面积,area,函数类型,(float r),函数体return 语句,练习定义计算圆面积的函数,利用函数调用计算圆环面积,area,float,(float r),函数体return 语句,练习定义计算圆面积的函数,利用函数调用计算圆环面积,area,float,(float r),float a;a=3.14159*r*r;
6、return a;,练习定义计算圆面积的函数,利用函数调用计算圆环面积,float area (float r) float a;a=3.14159*r*r;return a; void main() float r,R,s;coutrR;s=area(R)-area(r);cout“圆环面积是:”sendl; ,调用函数格式,函数名(实参表) 有返回值,在表达式中调用 无返回值,单独成语句调用,调用时,注意参数传递! (值传递),实参将值副本传给形参,形参改变不影响实参。,例: void main() int a=3,b=4; swap(a,b); cout“a=”a“,b=”b; void
7、 swap(int x,int y) int temp; temp=x ; x=y ; y=temp; cout“x=”x“,y=”y; ,传值,执行函数,输出结果?,函数的原型说明,函数说明 定义性说明:函数的定义部分 原型说明:引用性说明,针对调用在前而函数定义在后的情况,void swap(int x,int y); void main() int a=3,b=4; swap(a,b); cout“a=”a“,b=”b; void swap(int x,int y) int temp; temp=x ; x=y ; y=temp; cout“x=”x“,y=”y; ,保证编译器遇到函数调
8、用语句前先看到关于函数的说明!,void swap(int ,int );,函数的设计,目的:为了方便多次使用相同的代码段(功能)而设计的。 确定函数的功能(单一功能) 可根据功能对函数命名 确定函数参数 要实现函数功能必须预知的条件(入口) 确定返回值 函数调用后应该得到的结果(出口) 有结果,定义返回值类型(单个结果) 做输出,无返回值类型,void(不可省),练习,编写一个判断素数的函数,如果是素数,返回1;如果不是素数,返回0。 求解20以内的素数和 求100以内的素数倒数和 验证哥德巴赫猜想:任意一个大于6的偶数都可以表示成两个素数之和。,20,函数的嵌套和递归调用,VC+不允许在一
9、个函数体内再定义另一个函数,任一函数的定义均是独立的,函数之间是平等的和平行的,即不能嵌套定义函数。但是可以嵌套调用函数,即在一个函数中又调用另一个函数!,main函数,调用a函数,a函数,调用b函数,b函数,a函数结束,b函数结束,main结束,21,递归调用,递归调用 直接递归:在函数A的定义中出现调用A的情况 间接递归:在函数A的定义中调用B函数,而在B函数的定义中调用A函数,#include long int f(int n) if(n=0|n=1) return 1;return n*f(n-1); void main() coutf(5); ,【例】有5个人: 第5个人说他比第4个
10、人大2岁, 第4个人说他比第3个人大2岁, 第3个人说他比第2个人大2岁, 第2个人说他比第1个人大2岁, 第1个人说他10岁。求第5个人多少岁。,分析:age(n)= 10 (n=1) age(n)= age(n-1)+2 (n1),课堂练习,23,课堂练习,void f ( int b,int t ) int m;if ( bt ) m = (b+t)/2;coutm;f ( b,m-1);f ( m+1,t);void main() f (1 ,6 );,内联函数,内联函数 把函数体的代码直接插入到调用处,将调用函数的方式改为顺序执行直接插入的程序代码,这样可减少程序的执行时间 实质:以
11、空间换时间 要求 函数定义前加关键字inline 函数体内含循环,switch和复杂的if嵌套语句不要使用,25,参数特殊的函数,具有缺省参数值的函数在调用函数时,若明确给出了实参的值,则使用相应实参的值;若没有给出相应的实参,则使用缺省的值。,int sum ( int n=20 )int s=0, i;for(i=1;im; sum(m); sum( ); ,26,具有缺省参数值的函数,要求 缺省参数的说明必须出现在函数调用之前: 函数的定义放在最前面; 先给出原型说明,在其中依次列出参数的缺省值, 不能重复指定;例如: int sum(int n=20); 或者 int sum(int=
12、20); 参数的缺省值可以是表达式,但表达式用到的量必须有确定的值; 具有缺省值的参数可有多个,必须位于参数表的最右边。例:float V(float a,float b=10, float c=20),函数重载,指不同功能的函数可以具有相同的函数名。#includeint sum(int x,int y) return (x+y); float sum(float x,float y) return (x+y);,要求: 1、定义的重载函数必须具有不同的参数个数或不同的参数类型; 2、仅返回值不同不能定义为重载函数,void main(void) coutsum(3,5);coutsum(1
13、.0,3.5);,28,作用域,定义 程序中所说明的标识符在哪一个区间内有效,即在哪一个区域内可以使用或引用该标识符。 分类 块作用域 文件作用域 函数原型作用域 函数作用域 类作用域,29,块作用域,块 用花括号括起来的一部分程序称为一个块。 块作用域在块内说明的标识符只能在该块内使用。 局部变量 在一个函数内部定义的变量或在一个块中定义的变量。,30,块作用域,例如: float f1(void) int a,b; int c;c=a+b;,c只在该复合语句中有效,在f1函数内a,b始终 有效,31,块作用域,float f1(void) int a,b; int c;c=a+b;,当标识
14、符具有不同的作用域时,允许标识符同名,int c;,局部优先,32,块作用域,注意: 在for语句中说明的循环控 制变量具有块作用域,其 作用域为包含for语句的那 个内层块,而不仅作用于 for语句;,void main(void) for (int i=0; i5; i+) int j=0;cout+jt;cout“i=“i; ,33,函数原型作用域,函数原型作用域函数原型声明中参数表说明的标识符所具有的作用域 缺省值从其说明处开始到函数原型说明的结束处结束。例如:void sum(int x, int y);,34,函数作用域,函数作用域在函数内定义的标识符在其定义的函数内均有效,即不论
15、在函数内的某一地方定义,均可引用这种标识符。 缺省值只有标号具有函数作用域。例如: void f(int x) if(x0) goto label;return x;label: return x;,35,文件作用域,全局变量在函数外定义的变量或用extern说明的变量。 文件作用域全局变量的作用域称为文件作用域。 缺省值从全局变量定义的地方开始到源文件结束注意:在块作用域内变量与全局变量同名时,局部变量优先,但可在块作用域内通过作用域运算符“:”引用与局部变量同名的全局变量,36,文件作用域,例: #includeint x=100;void main(void) int x=200;x+=
16、:x;int x=500;:x+=x;coutxendl;cout:xendl;,37,文件作用域,例: #includeint a=2;void main(void) int b=2,c=3;+a; c+=+b;if (+a|+b|+c)couta b cendl;int a=3,c;c=b*3; a+=c;couta b cendl; a+=c;couta b cendl;,3 63 9 10 3 6,逻辑运算优化,求逻辑表达式的过程中,一旦能确定表达式的值,就不必再逐步求值了! if (+a|+b|+c)当逻辑表达式中有改变变量值的运算时,尤其要注意这种优化带来的副作用!,a先自增1,结
17、果非0,则该逻辑运算表达式结果为1。表达式计算结束!,逻辑运算优化,练习:下列表达式中,a、b、c的初值均为0,执行表达式后, a、b、c的值为多少?c=a+|+b|c+; a:1、b:1、c:1 c=+a a:1、b:1、c:0,40,存储类,存储类规定了变量的生存期,即何时为变量分配内存空间以及何时回收为变量分配的内存空间。 程序在内存中占用的存储空间分为三部分:分类 自动类型、静态类型、寄存器类型、外部类型,41,自动类型变量(auto),动态存储变量,当执行到变量作用域开始处,动态地分配存储空间,当执行到结束变量作用域处收回空间。 C+默认局部变量为auto,若未明确赋值,变量的初值不
18、定,全局变量不能为auto。,42,静态类型变量(static),静态类型变量均有确定的初值,当说明变量未指定初值时,编译器设其初值为0; 在程序开始执行时为变量分配空间并赋初值,空间保留至程序结束。,int t ( ) static int i=100;i+=5;return i;void main ( ) cout“i=”t()endl;cout“i=”t()endl; ,可否用i代替?,【例】求程序运行结果 int f(int a) int b=0; static int c=3; b+; c+; return a+b+c; void main() int a=2,i; for (i=0
19、;i3;i+) coutsetw(4)f(a); ,2,0,01,4,7,1,01,5,8,2,01,6,9,【结果】 7 8 9,静态类型变量(static),【例】求程序运行结果 int f(int a,int b) static int m=0,i=2; i+=m+1; m=i+a+b; return m; void main() int k=4,m=1,p; p=f(k,m); coutp; p=f(k,m); coutp; ,4,1,4,1,23,08,4,1,4,1,312,817,【结果】 8,17,静态类型变量(static),45,寄存器类型变量(用register修饰的局部
20、变量),动态存储方式,不为该类型的变量分配内存空间,尽可能直接分配使用CPU上的寄存器,主要用于控制循环次数的临时变量。,46,外部类型变量(用extern修饰的变量),外部类型变量一定是全局变量; 变量值存放在主存储器的静态存储区 程序执行开始至结束,始终占用该存储空间 定义 同一个源程序文件中定义的全局变量,定义性说明在后而使用在前时; 若干个文件组成一个完整程序时,在一个源文件中定义的全局变量被其他若干个源程序文件引用时,引用的文件使用外部说明语句来说明。,【例】求程序运行结果 void num() extern int x,y; int a=15,b=10; x=a-b; y=a+b;
21、 int x,y; void main() int a=7,b=5; x=a+b; y=a-b; num(); coutx , y ; ,【讨论】 如果第二行不加上extern呢?结果?,结果:5 , 25,结果:12 , 2,48,编译预处理,编译预处理 在编译源程序之前,由单独的编译预处理程序对源程序所做的加工处理过程; 编译预处理不属于C+语法范畴,所以在编译预处理指令前加“#”与C+语句区分开; 每条预处理指令单独占一行。 编译预处理指令分类 文件包含 宏定义 条件编译,49,文件包含,文件包含指将另外的文件包含到本文件之中. #include:从系统指定的方式检索,通常是C+预定义的
22、文件; #include “文件名”:从当前工作目录开始查找,通常是用户自定义的文件;,50,宏定义,不带参数的宏定义用一个指定的标识符(名字)来代表一个字符或字符串;#define PI 3.14159area=PI*r*r; 宏代换为:area=3.14159*r*r; 注意: #define出现在函数外,有效范围为定义后到源文件结束; 宏名出现在字符串不代换; 可引用已定义的宏名,层层转换。 例如: #define R 3.0#define PI 3.14159#define L 2*PI*R,宏定义命令不是C+语句,不允许命令后有分号,如果 #define PI 3.14159; 则area=3.14159;*r*r; 宏名做代换后才进行语法检查: #define PI 3.1a159 则area=3.1a159*r*r;,51,宏定义,带参数的宏定义:不是进行简单的字符串替换,还要进行参数替换。 例:#define R(a,b) a*barea=R(3,2); /area=2*3; 例: #define PI 3.14159#define S( r ) PI*r*rarea=S(a+b) ; /area=PI*a+b*a+b;area=S(a+b); /area=PI*(a+b)*(a+b);,