Presentation is loading. Please wait.

Presentation is loading. Please wait.

Chap 5 函数 5.1 计算圆柱体积 5.2 数字金字塔 5.3 复数运算.

Similar presentations


Presentation on theme: "Chap 5 函数 5.1 计算圆柱体积 5.2 数字金字塔 5.3 复数运算."— Presentation transcript:

1 Chap 5 函数 5.1 计算圆柱体积 5.2 数字金字塔 5.3 复数运算

2 本章要点 函数的作用?如何确定函数功能? 怎样定义函数?如何调用函数?定义函数与声明函数有何区别? 什么是函数的参数?怎样确定函数的参数?
在函数调用时,参数是如何传递数据的? 变量与函数有什么关系?如何使用局部变量和全局变量? 什么是静态变量?

3 5.1 计算圆柱体积 程序解析 函数的定义 函数的调用 函数程序设计

4 5.1.1 程序解析-计算圆柱体积 例5-1 输入圆柱体的高和半径,求圆柱体积,volume=π*r2*h。
程序解析-计算圆柱体积 例5-1 输入圆柱体的高和半径,求圆柱体积,volume=π*r2*h。 要求定义和调用函数cylinder (r, h )计算圆柱体的体积。

5 例5-1源程序 /* 计算圆柱体积 */ #include <stdio.h> int main( void ) {
double height, radius, volume; double cylinder (double r, double h); /* 函数声明*/ printf ("Enter radius and height: "); scanf ("%lf%lf", &radius, &height); /* 调用函数,返回值赋给volume */ volume = cylinder (radius, height ); printf ("Volume = %.3f\n", volume); return 0; }

6 例5-1源程序 /* 定义求圆柱体积的函数 */ double cylinder (double r, double h) {
Enter radius and height: Volume = /* 定义求圆柱体积的函数 */ double cylinder (double r, double h) { double result; result = * r * r * h; /* 计算体积 */ return result; /* 返回结果 */ }

7 例5-1源程序 #include <stdio.h> Enter radius and height: 3.0 10
int main( void ) { double height, radius, volume; double cylinder (double r, double h); /* 函数声明*/ printf ("Enter radius and height: "); scanf ("%lf%lf", &radius, &height); volume = cylinder (radius, height ); printf ("Volume = %.3f\n", volume); return 0; } Enter radius and height: Volume = 问题: 函数是如何运行的? double cylinder (double r, double h) { double result; result = * r * r * h; return result; }

8 5.1.2 函数的定义 函数是指完成一个特定工作的独立程序模块。 main()也是一个函数,C程序由一个main()或多个函数构成。
函数的定义 函数是指完成一个特定工作的独立程序模块。 库函数:由C语言系统提供定义 如scanf()、printf()等函数 自定义函数:需要用户自己定义 如计算圆柱体体积函数cylinder() main()也是一个函数,C程序由一个main()或多个函数构成。 程序中一旦调用了某个函数,该函数就会完成特定的计算,然后返回到调用它的地方。 函数经过运算,得到一个明确的运算结果,并需要回送该结果。例如,函数cylinder()返回圆柱的体积。

9 5.1.2 函数定义 函数返回值的类型 函数类型 函数名(形参表) /* 函数首部 */ { /* 函数体 */ 函数实现过程
double cylinder (double r, double h) { double result; result = * r * r * h; return result; } 5.1.2 函数定义 函数返回值的类型 没有分号 函数类型 函数名(形参表) /* 函数首部 */ { /* 函数体 */ 函数实现过程 return 表达式; } 只能返回一个值 把函数运算的结果回送给主函数

10 分析函数的定义 函数类型 函数名 形参表 double cylinder (double r, double h) /* 函数首部 */
{ /* 函数体,写在一对大括号内 */ double result; result = * r * r * h; /* 计算圆柱体积 */ return result; /* 返回运算结果*/ } 与函数类型一致

11 形参 函数类型 函数名(形参表){ 不能写成 double r, h 函数实现过程 return 表达式; }
double cylinder (double r, double h) { double result; result = * r * r * h; return result; } 类型1 参数1 ,类型2 参数2 ,……,类型n 参数n 参数之间用逗号分隔,每个参数前面的类型都必须分别写明

12 5.1.3 函数的调用 定义一个函数后,就可以通过程序来调用这个函数。
函数的调用 定义一个函数后,就可以通过程序来调用这个函数。 调用标准库函数时,在程序的最前面用#include命令包含相应的头文件。 调用自定义函数时,程序中必须有与调用函数相对应的函数定义。

13 1.函数调用的形式 函数调用的一般形式为: 对于实现计算功能的函数,函数调用通常出现在两种情况: 函数名(实际参数表) 赋值语句
volume = cylinder(radius, height ); 输出函数的实参 printf(“%f”, cylinder(radius, height ) );

14 2. 函数调用的过程 计算机在执行程序时,从主函数main开始执行,如果遇到某个函数调用,主函数被暂停执行,转而执行相应的函数,该函数执行完后,将返回主函数,然后再从原先暂停的位置继续执行。 函数遇return返回主函数

15 分析函数调用的过程 #include <stdio.h> int main( void )
{ double height, radius, volume; double cylinder (double r, double h); printf ("Enter radius and height: "); scanf ("%lf%lf", &radius, &height); volume = cylinder (radius, height ); printf ("Volume = %.3f\n", volume); return 0; } double cylinder (double r, double h) { double result; result = * r * r * h; return result; 调用函数 实参形参 执行函数中的语句 返回调用它的地方

16 3.参数传递 函数定义时的参数被称为形式参数(简称形参) 函数调用时的参数被称为实际参数(简称实参) 参数传递:实参形参 单向传递
double cylinder (double r, double h); 函数调用时的参数被称为实际参数(简称实参) volume = cylinder (radius, height); 参数传递:实参形参 在参数传递过程中,实参把值复制给形参。 形参和实参一一对应:数量一致,类型一致,顺序一致 形参:变量,用于接受实参传递过来的值 实参:常量、变量或表达式 单向传递

17 4.函数结果返回 完成确定的运算,将运算结果返回给主调函数。 函数结果返回的形式: return 表达式; return (表达式);

18 【例5-2】定义判断奇偶数的函数even (n)
定义一个判断奇偶数的函数even (n),当n为偶数时返回1,否则返回0。 /* 判断奇偶数的函数 */ int even (int n) /* 函数首部 */ { if(n%2 == 0) /* 判别奇偶数 */ return 1; /* 偶数返回1 */ else return 0; /* 奇数返回0 */ } 如何调用该函数? #include <stdio.h> int main( void ) { int x,sum=0 ; 。。。。 if (even(x)==1) sum=sum+x ; printf(“%d”, sum); return 0; }

19 只写函数定义中的第1行(函数首部),并以分号结束。
5.函数原型声明 只写函数定义中的第1行(函数首部),并以分号结束。 函数类型 函数名(参数表); double cylinder (double r, double h); void pyramid (int n); 函数必须先定义后调用,将主调函数放在被调函数的后面,就像变量先定义后使用一样。 如果自定义函数在主调函数的后面,就需要在函数调用前,加上函数原型声明。 函数声明:说明函数的类型和参数的情况,以保证程序编译时能判断对该函数的调用是否正确。

20 §5.1.3 函数调用 小结: 给相应位置的形参;函数执行完后,通过 return( ),可返回一个结果值。 要调用函数, 必须先要声明!
在执行函数调用时,实参把值计算出来,拷贝 给相应位置的形参;函数执行完后,通过 return( ),可返回一个结果值。 实参与形参 有多个实参时 形参的改变 个数相同、类型一致 后面的先计算 不影响实参 变量的值 只能返回一个结果, 类型与函数定义时一致

21 5.1.4 函数程序设计 例5-3 输入精度e,使用格里高利公式求π的近似值,精确到最后一项的绝对值小于e。要求定义和调用函数 funpi(e) 求π的近似值。 什么做参数?

22 例5-4 源程序 #include <stdio.h> #include <math.h>
int main (void) { double e, pi; double funpi (double e); printf ("Enter e:"); scanf ("%lf", &e); pi = funpi (e); printf ("pi = %f\n", pi); return 0; } Enter e: pi = double funpi (double e) { int denominator, flag; double item, sum; flag = 1; denominator = 1; item = 1.0; sum = 0; while (fabs (item) >= e){ item = flag * 1.0 / denominator; sum = sum + item; flag = -flag; denominator = denominator + 2; } return sum * 4;

23 例5-4 判断素数的函数 例5-5 求100以内的全部素数,每行输出10个。素数就是只能被1和自身整除的正整数,1不是素数,2是素数。
例5-4 判断素数的函数 例5-5 求100以内的全部素数,每行输出10个。素数就是只能被1和自身整除的正整数,1不是素数,2是素数。 要求定义和调用函数prime (m)判断m是否为素数,当m为素数时返回1,否则返回0。 算法描述:对2~100之间的每个数进行判断,若是素数,则输出该数。 for(m = 2; m <= 100; m++) if (m是素数) printf("%d ", m); prime(m) != 0

24 例5-4 源程序 int prime (int m) { int i, n; if ( m == 1 ) return 0;
#include <stdio.h> #include <math.h> int main(void) { int count, m; int prime (int m); count = 0; for(m = 2; m <= 100; m++){ if ( prime(m) != 0 ){ printf("%6d", m ); count++; if (count %10 == 0) printf ("\n"); } int prime (int m) { int i, n; if ( m == 1 ) return 0; n = sqrt (m); for( i = 2; i <= n; i++) if (m % i == 0){ return 0; } return 1;

25 5.2 数字金字塔 程序解析 不返回结果的函数 结构化程序设计思想

26 例5-5 输出5之内的数字金字塔。 for (i = 1; i <= n; i++) {
for (j = 1; j <= n-i; j++) printf(“ ”); 一行中的数字显示 } for (i = 1; i <= n; i++) { 一行的处理 } 例5-5 输出5之内的数字金字塔。 一行中的空格处理; 一行中的数字显示 } /* 输出数字金字塔 */ #include <stdio.h> int main (void) { void pyramid (int n); /* 函数声明 */ pyramid(5); /* 调用函数,输出数字金字塔 */ return 0; } void pyramid (int n) /* 函数定义 */ int i, j; for (i = 1; i <= n; i++){ /* 需要输出的行数 */ for (j = 1; j <= n-i; j++) /* 输出每行左边的空格 */ printf(" "); for (j = 1; j <= i; j++) /* 输出每行的数字 */ printf(" %d ", i); /* 每个数字的前后各有一个空格 */ putchar ('\n'); 1 2 2

27 5.2.2 不返回运算结果的函数定义 表示不返回结果 不能省略, 否则 函数类型被默认定义为int
不返回运算结果的函数定义 表示不返回结果 void 函数名(参数表) /* 函数首部 */ { /* 函数体 */ 函数实现过程 return; /* 可以省略return */ } 这类函数通常用于屏幕输出等 不能省略, 否则 函数类型被默认定义为int

28 5.2.2 不返回运算结果的函数定义 由于函数没有返回结果,函数调用不可能出现在表达式中,通常以独立的调用语句方式,如pyramid(5);
不返回运算结果的函数定义 由于函数没有返回结果,函数调用不可能出现在表达式中,通常以独立的调用语句方式,如pyramid(5); 不返回结果的函数,在定义、调用、参数传递、函数声明上,思路完全与以前相同,只是函数类型变为void。 它适用把一些确定的、相对独立的程序功能包装成函数。 主函数通过调用不同的函数,体现算法步骤 各步骤的实现由相应函数完成 简化主函数结构,以体现结构化程序设计思想。

29 5.2.3 结构化程序设计思想 结构化程序设计(Structured Programming)
结构化程序设计思想 结构化程序设计(Structured Programming) 程序设计技术 C语言是结构化程序设计语言 强调程序设计的风格和程序结构的规范化,提倡清晰的结构 基本思路是将一个复杂问题的求解过程划分为若干阶段,每个阶段要处理的问题都容易被理解和处理。 按自顶向下的方法对问题进行分析、模块化设计和结构化编码等3个步骤。

30 1. 自顶向下的分析方法 把大的复杂的问题分解成小问题后再解决
面对一个复杂的问题,首先进行上层(整体)的分析,按组织或功能将问题分解成子问题 如果子问题仍然十分复杂,再做进一步分解,直到处理对象相对简单,容易处理为止。 当所有的子问题都得到了解决,整个问题也就解决了。 每一次分解都是对上一层的问题进行细化和逐步求精,最终形成一种类似树形的层次结构,来描述分析的结果。

31 学生成绩统计程序的层次结构图 学生成绩统计程序 成绩输入 数据计算 数据查找 输出成绩 计算学生平均分 计算课程平均分 模块用函数实现

32 3. 结构化编码主要原则 经模块化设计后,每一个模块都可以独立编码。编程时应选用顺序、选择和循环三种控制结构
对变量、函数、常量等命名时,要见名知意,有助于对变量含义或函数功能的理解。 在程序中增加必要的注释,增加程序的可读性。 要有良好的程序视觉组织,利用缩进格式 程序要清晰易懂,语句构造要简单直接 程序有良好的交互性,输入有提示,输出有说明

33 5.3 复数运算 程序解析 局部变量和全局变量 变量生命周期和静态局部变量

34 例5-6 分别输入2个复数的实部与虚部,用函数实现计算2个复数之和与之积。
例5-6 分别输入2个复数的实部与虚部,用函数实现计算2个复数之和与之积。 分析 若2个复数分别为: c1=x1+y1i , c2=x2+y2i, 则: c1+c2 = (x1+x2) + (y1+y2)i c1*c2 = (x1*x2-y1*y2) + (x1*y2+x2*y1)i

35 Enter 1st complex number(real and imaginary):1 1
运行结果 Enter 1st complex number(real and imaginary):1 1 Enter 2nd complex number(real and imaginary):-2 3 addition of complex is i product of complex is i #include<stdio.h> float result_real, result_imag; /* 全局变量,用于存放函数结果 */ int main(void) { float imag1, imag2, real1, real2; /* 两个复数的实、虚部变量 */ /* 函数声明 */ void complex_prod(float real1, float imag1, float real2, float imag2); void complex_add(float real1, float imag1, float real2, float imag2); printf("Enter 1st complex number(real and imaginary): "); scanf("%f%f", &real1, &imag1); /* 输入第一个复数 */ printf("Enter 2nd complex number(real and imaginary): "); scanf("%f%f", &real2, &imag2); /* 输入第两个复数 */ complex_add(real1, imag1, real2, imag2); /* 求复数之和 */ printf("addition of complex is %f+%fi\n", result_real, result_imag); complex_prod(real1, imag1, real2, imag2); /* 求复数之积 */ printf("product of complex is %f+%fi\n", result_real, result_imag); return 0; }

36 void complex_add(float real1, float imag1, float real2, float imag2)
{ result_real = real1 + real2; result_imag = imag1 + imag2; } void complex_prod(float real1, float imag1, float real2, float imag2) { result_real = real1*real2 - imag1*imag2; result_imag = real1*imag2 + real2*imag1; }

37 5.3.2 局部变量和全局变量 局部变量 全局变量 在函数内定义的变量(包括形参) 定义在复合语句内的变量
作用范围:本函数内部 定义在复合语句内的变量 作用范围:复合语句内部 全局变量 在函数以外定义的变量,不从属于任一函数。 作用范围:从定义处到源文件结束(包括各函数)

38 例5-6 在复合语句中定义局部变量。 改成b会如何? #include <stdio.h> int main (void)
例5-6 在复合语句中定义局部变量。 #include <stdio.h> int main (void) { int a; a = 1; { /* 复合语句开始 */ int b = 2; b = a + b; a = a + b; } /* 复合语句结束 */ printf ("%d " , a ); return 0; } 输出: 4 b:小范围内的临时变量 改成b会如何?

39 例5-7 全局变量定义 若局部变量与全局变量同名,局部变量优先 输出: 4, 7 #include "stdio.h"
例5-7 全局变量定义 #include "stdio.h" int x; /* 定义全局变量x */ int f( ) { int x = 4; /* x为局部变量 */ return x; } int main(void) int a = 1; x = a; /* 对全局变量 x 赋值 */ a = f( ); /* a的值为4 */ int b = 2; b = a + b; /* b的值为4 */ x = x + b; /* 全局变量运算 */ printf("%d %d" , a, x); return 0; 若局部变量与全局变量同名,局部变量优先 输出: 4, 7

40 变量作用范围示例 x=? a=? b=? b=? x=5 b=6 t=4 a没定义 x=? b=? t=? a=? int x=1;
void main( ) { int a=2; …….. { int b=3; ….. } f( ); ……….. int t=4 ; void f( ) { int x=5, b=6; ……. int a=7; x=? a=? b=? b=? x=5 b=6 t=4 a没定义 x=? b=? t=? a=?

41 【例5-8】 用函数实现财务现金记账。先输入操作类型(1收入,2支出,0结束),再输入操作金额,计算现金剩余额,经多次操作直到输入操作为0结束。要求定义并调用函数,其中现金收入与现金支出分别用不同函数实现。 分析: 设变量cash保存现金余额值,由于它被主函数、现金收入与现金支出函数共用,任意使用场合其意义与数值都是明确和唯一的,因此令其为全局变量。

42 void income(float number) { cash = cash + number; /* 改变全局变量cash */ }
/* 定义计算现金收入函数 */ void income(float number) { cash = cash + number; /* 改变全局变量cash */ } /* 定义计算现金支出函数 */ void expend(float number) { cash = cash - number; /* 改变全局变量cash */ #include<stdio.h> float cash; /* 定义全局变量,保存现金余额 */ int main(void) { int choice; float value; void income(float number), expend(float number); /* 函数声明 */ cash = 0; /* 初始金额=0 */ printf("Enter operate choice(0--end, 1--income, 2--expend):"); scanf("%d", &choice); /* 输入操作类型 */ while (choice != 0){ /* 若输入类型为0,循环结束 */ if (choice == 1 || choice == 2) { printf("Enter cash value:"); /* 输入操作现金额 */ scanf("%f", &value); if (choice == 1) income(value); /* 函数调用,计算现金收入 */ else expend(value); /* 函数调用,计算现金支出 */ printf("current cash:%.2f\n", cash); } scanf("%d", &choice); /* 继续输入操作类型 */ return 0; Enter operate choice(0--end, 1--income, 2--expend):1 Enter cash value:1000 current cash: Enter operate choice(0--end, 1--income, 2--expend):2 Enter cash value:456 current cash: Enter operate choice(0--end, 1--income, 2--expend):0

43 5.3.2 局部变量和全局变量 讨论 引起注意 全局变量比局部变量自由度大,更方便 ?
对于规模较大的程序,过多使用全局变量会带来副作用,导致各函数间出现相互干扰。如果整个程序是由多人合作开发,各人都按自己的想法使用全局变量,相互的干扰可能会更严重。 因此在变量使用中,应尽量使用局部变量,从某个角度看使用似乎受到了限制,但从另一个角度看,它避免了不同函数间的相互干扰,提高了程序质量。

44 5.3.3 变量生命周期和静态局部变量 变量生命周期 自动变量(auto): 普通的局部变量 函数调用时,定义变量,分配存储单元。
变量生命周期和静态局部变量 变量生命周期 变量从定义开始分配存储单元,到运行结束存储单元被回收的整个过程。 自动变量(auto): 普通的局部变量 int x, y;  auto int x, y; char c1;  auto char c1; 函数调用时,定义变量,分配存储单元。 函数调用结束,收回存储单元。 全局变量:从程序执行开始,到程序的结束,存储单元始终保持。

45 C程序存储分布示意图(例5-6)

46 静态局部变量 static 类型名 变量表 作用范围:局部变量 生命周期:全局变量

47 【例5-9】输入正整数n,输出1!~n!的值。要求定义并调用含静态变量的函数fact_s(n)计算n!。
double fact_s(int n) { static double f = 1; /* 定义静态变量,第一次赋值为1 */ f = f * n; /* 在上一次调用时的值上乘n */ return(f); } 【例5-9】输入正整数n,输出1!~n!的值。要求定义并调用含静态变量的函数fact_s(n)计算n!。 #include <stdio.h> double fact_s(int n); int main(void) { int i, n; printf("Input n:"); scanf("%d", &n); for(i=1; I <= n; i++) printf("%3d!=%.0f\n", i, fact_s(i)); /* 输出i和i! */ return 0; } fact_s()函数中并没有循环语句,它是靠静态变量f保存着上次函数调用时,计算得到的(n-1)!值,再乘上n,实现n!的计算。

48 静态局部变量 自动变量如果没有赋初值,其存储单元中将是随机值。 就静态变量而言,如果定义时没有赋初值,系统将自动赋0。
赋初值只在函数第一次调用时起作用,以后调用都按前一次调用保留的值使用。 静态局部变量受变量作用范围限制,不能作用于其他函数(包括主函数)。

49 静态局部变量 静态变量与全局变量均位于静态存储区 他们的共同点是生命周期贯穿整个程序执行过程。
区别在于作用范围不同,全局变量可作用于所有函数,静态变量只能用于所定义函数,而不能用于其他函数。

50 本章小结 系统介绍函数的定义和函数调用 函数与变量间的关系,不同形式的变量在函数中起的作用不同。
学习如何针对具体问题,确定需要使用函数的功能要求,再将功能用函数程序实现 考虑如何调用定义好的函数,实现主调函数与被调函数的连接 确定参数功能,掌握参数的传递实现 函数与变量间的关系,不同形式的变量在函数中起的作用不同。 局部变量、全局变量和静态变量


Download ppt "Chap 5 函数 5.1 计算圆柱体积 5.2 数字金字塔 5.3 复数运算."

Similar presentations


Ads by Google