第 9 章 函数的高级应用

33
9 9 第 第第第第第第第 第 第第第第第第第 C C 语语语语语语 语语语语语语

description

C 语言程序设计. 第 9 章 函数的高级应用. 本章内容. 递归与递归函数 指向函数的指针 返回指针值的函数. 递归问题的提出. “汉诺塔”( Hanoi ) 这是一个必须用递归方法才能解决的问题 n=64 时, 18,446,744,073,709,551,615 次 1844 亿亿次 每次 1 微秒,需要 60 万年. 递归问题的提出. n=3. A→C , A→B , C→B , A→C , B→A , B→C , A→C. A. B. C. 递归问题的提出. A→C , A→B , C→B , A→C , B→A , B→C , A→C. - PowerPoint PPT Presentation

Transcript of 第 9 章 函数的高级应用

Page 1: 第 9 章 函数的高级应用

第第 99 章 函数的高级应用章 函数的高级应用 第第 99 章 函数的高级应用章 函数的高级应用

CC 语言程序设计语言程序设计

Page 2: 第 9 章 函数的高级应用

23/4/20C语言大学实用教程课件 制作人 苏小红

2

本章内容本章内容本章内容本章内容

递归与递归函数递归与递归函数 指向函数的指针指向函数的指针 返回指针值的函数 返回指针值的函数

Page 3: 第 9 章 函数的高级应用

23/4/20C语言大学实用教程课件 制作人 苏小红

3

递归问题的提出 递归问题的提出 递归问题的提出 递归问题的提出 ““ 汉诺塔”(汉诺塔”( HanoiHanoi ))

– 这是一个必须用递归方法才能解决的问题这是一个必须用递归方法才能解决的问题

– n=64n=64 时, 时, 18,446,744,073,709,551,61518,446,744,073,709,551,615 次次– 18441844 亿亿次亿亿次– 每次每次 11 微秒,需要微秒,需要 6060 万年万年

Page 4: 第 9 章 函数的高级应用

23/4/20C语言大学实用教程课件 制作人 苏小红

4

递归问题的提出 递归问题的提出 递归问题的提出 递归问题的提出

A→CA→C ,, A→BA→B ,, C→BC→B ,,A→CA→C ,, B→AB→A ,, B→CB→C ,, A→CA→C

AA BB CC

n=3n=3

Page 5: 第 9 章 函数的高级应用

23/4/20C语言大学实用教程课件 制作人 苏小红

5

递归问题的提出 递归问题的提出 递归问题的提出 递归问题的提出

A→CA→C ,, A→BA→B ,, C→BC→B ,,A→CA→C ,, B→AB→A ,, B→CB→C ,, A→CA→C

AA BB CC

Page 6: 第 9 章 函数的高级应用

23/4/20C语言大学实用教程课件 制作人 苏小红

6

递归问题的提出 递归问题的提出 递归问题的提出 递归问题的提出

A→CA→C ,, A→BA→B ,, C→BC→B ,,A→CA→C ,, B→AB→A ,, B→CB→C ,, A→CA→C

AA BB CC

Page 7: 第 9 章 函数的高级应用

23/4/20C语言大学实用教程课件 制作人 苏小红

7

递归问题的提出 递归问题的提出 递归问题的提出 递归问题的提出

A→CA→C ,, A→BA→B ,, C→BC→B ,,A→CA→C ,, B→AB→A ,, B→CB→C ,, A→CA→C

AA BB CC

Page 8: 第 9 章 函数的高级应用

23/4/20C语言大学实用教程课件 制作人 苏小红

8

递归问题的提出 递归问题的提出 递归问题的提出 递归问题的提出

A→CA→C ,, A→BA→B ,, C→BC→B ,,A→CA→C ,, B→AB→A ,, B→CB→C ,, A→CA→C

AA BB CC

n 更大些怎么办?

Page 9: 第 9 章 函数的高级应用

23/4/20C语言大学实用教程课件 制作人 苏小红

9

递归问题的提出递归问题的提出递归问题的提出递归问题的提出 第一步:将问题简化。第一步:将问题简化。

– 假设假设 AA 杆上只有杆上只有 22 个圆盘,即汉诺塔有个圆盘,即汉诺塔有 22 层,层, nn== 22 。。

AA BB CC

Page 10: 第 9 章 函数的高级应用

23/4/20C语言大学实用教程课件 制作人 苏小红

10

递归问题的提出递归问题的提出递归问题的提出递归问题的提出

对于一个有 对于一个有 nn (( n>1n>1 )个圆盘的汉诺塔,将)个圆盘的汉诺塔,将 nn 个圆盘个圆盘分为两部分:上面的 分为两部分:上面的 n-1 n-1 个圆盘和最下面的个圆盘和最下面的 nn 号圆号圆盘。将“上面的盘。将“上面的 n-1n-1 个圆盘”看成一个整体。个圆盘”看成一个整体。– 将 将 n-1n-1 个盘子从一根木桩移到另一根木桩上个盘子从一根木桩移到另一根木桩上– 将将 11 个盘子从一根木桩移到另一根木桩上个盘子从一根木桩移到另一根木桩上

A CCB

Page 11: 第 9 章 函数的高级应用

23/4/20C语言大学实用教程课件 制作人 苏小红

11

递归问题的提出递归问题的提出递归问题的提出递归问题的提出 将 将 nn 个盘子从一根木桩移到另一根木桩上个盘子从一根木桩移到另一根木桩上 问题分解为:问题分解为:

– 将 将 n-1n-1 个盘子从一根木桩上移到另一根木桩上个盘子从一根木桩上移到另一根木桩上– 将将 11 个盘子从一根木桩移到另一根木桩上个盘子从一根木桩移到另一根木桩上

设计一个函数,入口参数为设计一个函数,入口参数为 nn ::– 将 将 nn 个盘子从一根木桩移到另一根木桩上个盘子从一根木桩移到另一根木桩上

将 将 n-1n-1 个盘子从一根木桩上移到另一根木桩上个盘子从一根木桩上移到另一根木桩上– 也要调用这个函数来实现也要调用这个函数来实现– 出现了函数调用自己的问题出现了函数调用自己的问题

递归调用(递归调用( Recursive CallRecursive Call ) )

Page 12: 第 9 章 函数的高级应用

23/4/20C语言大学实用教程课件 制作人 苏小红

12

递归(递归( RecursionRecursion )函数)函数递归(递归( RecursionRecursion )函数)函数 递归函数递归函数

– 函数直接或间接调用自己函数直接或间接调用自己 直接调用方式:直接调用方式:

int f(x)int f(x) { { int y,z; int y,z; …. …. z=f(x); z=f(x); …… …… } }

Page 13: 第 9 章 函数的高级应用

23/4/20C语言大学实用教程课件 制作人 苏小红

13

例例 9.19.1 求整数求整数 nn 的阶乘的阶乘 nn ! ! 例例 9.19.1 求整数求整数 nn 的阶乘的阶乘 nn ! ! 计算计算 nn !! = = nn *( *(nn-1)*(-1)*(nn-2)*…*1 -2)*…*1

– 迭代法迭代法– 用递归的方法 用递归的方法

Page 14: 第 9 章 函数的高级应用

23/4/20C语言大学实用教程课件 制作人 苏小红

14

例例 9.19.1 求整数求整数 nn 的阶乘的阶乘 nn !!例例 9.19.1 求整数求整数 nn 的阶乘的阶乘 nn !!#include <stdio.h>#include <stdio.h>main()main(){{

int n, i;int n, i;long result=1;long result=1;printf("Input n:");printf("Input n:");scanf("%d", &n);scanf("%d", &n);

result = 1;result = 1;for (i=1; i<=n; i++)for (i=1; i<=n; i++)

result *= i;result *= i;

printf("%d!= %ld", n, result); printf("%d!= %ld", n, result); }}

迭代法迭代法

Page 15: 第 9 章 函数的高级应用

23/4/20C语言大学实用教程课件 制作人 苏小红

15

long fact(long n)long fact(long n) {{

long result;long result;

if (n < 0) if (n < 0) return –1;return –1; else if (n==0 || n==1) else if (n==0 || n==1) /*/* 递归终止条件递归终止条件 **//

return 1;return 1; elseelse return (n * fact(n-1));return (n * fact(n-1));/*/* 递归调用递归调用 **//

}}

例例 9.19.1 求整数求整数 nn 的阶乘的阶乘 nn !!例例 9.19.1 求整数求整数 nn 的阶乘的阶乘 nn !!

递归法递归法

Page 16: 第 9 章 函数的高级应用

23/4/20C语言大学实用教程课件 制作人 苏小红

16

#include <stdio.h>#include <stdio.h>long fact(long n);long fact(long n);

main()main(){{

intint n; n;long result;long result;

printf("Input n: ");printf("Input n: ");scanf("%d", &n);scanf("%d", &n);

result = fact(n); result = fact(n);

if(result==-1)if(result==-1)printf("n<0,data error ! \ n");printf("n<0,data error ! \ n");

elseelseprintf("%d! = %ld\n", n, result);printf("%d! = %ld\n", n, result);

}}

例例 9.19.1 求整数求整数 nn 的阶乘的阶乘 nn !!例例 9.19.1 求整数求整数 nn 的阶乘的阶乘 nn !!

递归法递归法

Page 17: 第 9 章 函数的高级应用

23/4/20C语言大学实用教程课件 制作人 苏小红

17

递归调用过程递归调用过程递归调用过程递归调用过程 执行过程:执行过程:

fact(5)=5*fact(4)=120 fact(5)=5*fact(4)=120 fact(4)=4*fact(3)=24fact(4)=4*fact(3)=24 fact(3)=3*fact(2)=6 fact(3)=3*fact(2)=6 fact(2)=2*fact(1)=2 fact(2)=2*fact(1)=2 fact(1)=1 fact(1)=1

mainmain fact(5)fact(5) fact(4)fact(4) fact(3)fact(3) fact(2)fact(2) fact(1)fact(1)

Page 18: 第 9 章 函数的高级应用

23/4/20C语言大学实用教程课件 制作人 苏小红

18

递归函数递归函数递归函数递归函数 递归方法的基本原理递归方法的基本原理

– 将复杂问题逐步化简,最终转化为一个最简单的问题将复杂问题逐步化简,最终转化为一个最简单的问题– 最简单问题的解决,就意味着整个问题的解决 最简单问题的解决,就意味着整个问题的解决

递归调用应该能够在有限次数内终止递归递归调用应该能够在有限次数内终止递归– 递归调用如果不加以限制,将无数次的循环调用递归调用如果不加以限制,将无数次的循环调用– 必须在函数内部加控制语句,只有当满足一定条件必须在函数内部加控制语句,只有当满足一定条件

时,递归终止时,递归终止– 有时将其称为条件递归有时将其称为条件递归

Page 19: 第 9 章 函数的高级应用

23/4/20C语言大学实用教程课件 制作人 苏小红

19

递归函数递归函数递归函数递归函数 任何一个递归调用程序必须包括两部分任何一个递归调用程序必须包括两部分

– 递归循环继续的过程递归循环继续的过程– 递归调用结束的过程递归调用结束的过程

if (if ( 递归终止条件成立递归终止条件成立 ))

return return 递归公式的初值递归公式的初值 ; ;

elseelse

return return 递归函数调用返回的结果值递归函数调用返回的结果值 ;;

Page 20: 第 9 章 函数的高级应用

23/4/20C语言大学实用教程课件 制作人 苏小红

20

递归函数递归函数递归函数递归函数 思考:下面程序有什么问题思考:下面程序有什么问题 阶乘函数的递归实现阶乘函数的递归实现 unsignedunsigned longlong FacFac((unsignedunsigned intint n) n)

{ { ifif (n < 0) (n < 0)

printf("data error!");printf("data error!");else ifelse if (n==0 || n==1) (n==0 || n==1)

returnreturn 1; 1;elseelse

returnreturn n * n * FacFac(n-1);(n-1); } }

编译这个程序也会给出警告,提示“非所有控制分支都有返回值”。编译这个程序也会给出警告,提示“非所有控制分支都有返回值”。 同时,运行程序后如果我们输入了一个负数,那么程序运行结果为:同时,运行程序后如果我们输入了一个负数,那么程序运行结果为: Input a:-1↙Input a:-1↙ Input data error!Input data error! a! = 11 a! = 11

如果某个函数需要返回值的话,那么一定要确保该函数中的所有控制分支都有返回

值。

Page 21: 第 9 章 函数的高级应用

23/4/20C语言大学实用教程课件 制作人 苏小红

21

递归函数递归函数递归函数递归函数 思考:下面程序有什么问题思考:下面程序有什么问题 阶乘函数的递归实现阶乘函数的递归实现 unsignedunsigned longlong FacFac((unsignedunsigned intint n) n)

{ { ifif (n < 0) (n < 0)

printf("data error!");printf("data error!");else ifelse if (n==0 || n==1) (n==0 || n==1)

returnreturn 1; 1;elseelse

returnreturn n * n * FacFac(n-1);(n-1); } }

存在死语句存在死语句

存在另一个隐蔽的错误

Page 22: 第 9 章 函数的高级应用

23/4/20C语言大学实用教程课件 制作人 苏小红

22

返回指针值的函数返回指针值的函数 返回指针值的函数返回指针值的函数 数据类型 数据类型 * * 函数名函数名 (( 参数表参数表 ))

{{

…… ……

} }

例例 9.3: 9.3:

编一个函数连接两个字符串,然后返回连接编一个函数连接两个字符串,然后返回连接后字符串的首地址后字符串的首地址

Page 23: 第 9 章 函数的高级应用

23/4/20C语言大学实用教程课件 制作人 苏小红

23

返回指针值的函数返回指针值的函数返回指针值的函数返回指针值的函数char *MyStrcat(char *dstStr, char *srcStr)char *MyStrcat(char *dstStr, char *srcStr){{

char *pStr = dstStr; char *pStr = dstStr;

while (*dstStr != '\0')while (*dstStr != '\0'){{ dstStr++;dstStr++;} }

for(; *srcStr!='\0'; dstStr++, srcStr++)for(; *srcStr!='\0'; dstStr++, srcStr++){{ *dstStr = *srcStr;*dstStr = *srcStr;}}*dstStr = '\0';*dstStr = '\0';return pStr; return pStr;

}}

Page 24: 第 9 章 函数的高级应用

23/4/20C语言大学实用教程课件 制作人 苏小红

24

返回指针值的函数返回指针值的函数返回指针值的函数返回指针值的函数#include<stdio.h>#include<stdio.h>

#define ARR_SIZE 80#define ARR_SIZE 80

char *MyStrcat(char *dstStr, char *srcStr); char *MyStrcat(char *dstStr, char *srcStr);

main()main(){{

char first[ARR_SIZE] = "Hello "; char first[ARR_SIZE] = "Hello "; char second[ARR_SIZE] = "world";char second[ARR_SIZE] = "world";char *result = NULL;char *result = NULL;

result = MyStrcat(first, second);result = MyStrcat(first, second);

printf("\nThe result is : %s\n", result);printf("\nThe result is : %s\n", result);}}

这个字符数组应该保证足够

Page 25: 第 9 章 函数的高级应用

23/4/20C语言大学实用教程课件 制作人 苏小红

25

函数指针 (函数指针 ( Function PointersFunction Pointers ) ) 函数指针 (函数指针 ( Function PointersFunction Pointers ) ) 编译器将不带编译器将不带 ()() 的函数名解释为该程序的入口地的函数名解释为该程序的入口地

址址– 换句话说,函数名是指向程序入口的指针换句话说,函数名是指向程序入口的指针

数据类型 数据类型 (* (* 指针名指针名 )();)(); 例如:例如: int (*p)(); int (*p)();

几个容易犯的错误:几个容易犯的错误:– 忘记了前一个忘记了前一个 ()() ,写成,写成

int *p();int *p(); 声明一个返回值是整型指针的函数,函数名为声明一个返回值是整型指针的函数,函数名为 pp

– 忘掉了后一个忘掉了后一个 ()() ,写成,写成 int (*p);int (*p); 定义了一个整型指针定义了一个整型指针

– 定义时后一个括号内参数类型与指向的函数不匹配定义时后一个括号内参数类型与指向的函数不匹配

Page 26: 第 9 章 函数的高级应用

23/4/20C语言大学实用教程课件 制作人 苏小红

26

函数指针函数指针函数指针函数指针 求下列函数的定积分 求下列函数的定积分

3

0 22

1

0

21

d1

d)1(

xx

xy

xxy

1

1

( )d ( ) ( ) ( ) ( 2 ) ( ( ) ) ( )2 2 2

( ) 2 ( ) 2 ( 2 ) 2 ( ( 1) ) ( )2

1( ( ) ( )) ( )

2

b

a

n

i

h h hf x x f a f a h f a h f a h f a n i h f b

hf a f a h f a h f a n h f b

h f a f b f a ih

Page 27: 第 9 章 函数的高级应用

23/4/20C语言大学实用教程课件 制作人 苏小红

27

函数指针函数指针函数指针函数指针 不用函数指针的编程方法 不用函数指针的编程方法 float IntegralFunfloat IntegralFun11(float a, float b)(float a, float b){{

float s,h,y;float s,h,y;int n,i;int n,i;

s = ((1.0+a*a) + (1.0+b*b)) / 2.0; s = ((1.0+a*a) + (1.0+b*b)) / 2.0; n = 100;n = 100;h = (b - a) / n;h = (b - a) / n;

for (i=0; i<n; i++)for (i=0; i<n; i++){{

y = a + i * h;y = a + i * h;s += 1.0 + y * y;s += 1.0 + y * y;

}}return s * h;return s * h;

}}

1

0

21 d)1( xxy

Page 28: 第 9 章 函数的高级应用

23/4/20C语言大学实用教程课件 制作人 苏小红

28

函数指针 函数指针 函数指针 函数指针 不用函数指针的编程方法 不用函数指针的编程方法 float IntegralFunfloat IntegralFun22(float a, float b)(float a, float b){{

float s,h,y;float s,h,y;int n,i;int n,i;

s = (a/(1.0+a*a) + b/(1.0+b*b)) / 2.0; s = (a/(1.0+a*a) + b/(1.0+b*b)) / 2.0; n = 100;n = 100;h = (b - a) / n;h = (b - a) / n;

for (i=0; i<n; i++)for (i=0; i<n; i++){{

y = a + i * h;y = a + i * h;s += y / (1.0 + y * y);s += y / (1.0 + y * y);

}}return s * h;return s * h;

}}

3

0 22 d1

xx

xy

Page 29: 第 9 章 函数的高级应用

23/4/20C语言大学实用教程课件 制作人 苏小红

29

函数指针函数指针函数指针函数指针 使用函数指针的编程方法使用函数指针的编程方法

float Integral(float Integral(float (*f)(float)float (*f)(float), float a, float b), float a, float b){{

float s, h, y;float s, h, y;int n, i;int n, i;

s = ((*f)(a) + (*f)(b)) / 2.0;s = ((*f)(a) + (*f)(b)) / 2.0;n = 100;n = 100;h = (b - a) / n;h = (b - a) / n;for (i=0; i<n; i++)for (i=0; i<n; i++){{

y = a + i * h;y = a + i * h; s += (*f)(y);s += (*f)(y);}}return s * h;return s * h;

} }

y1 = Integral(Fun2, 0.0, 3.0);y2 = Integral(Fun1, 0.0, 1.0);

float Fun1(float x) { return 1+x*x;}

float Fun2(float x) { return x/(1+x*x) ;}

Page 30: 第 9 章 函数的高级应用

23/4/20C语言大学实用教程课件 制作人 苏小红

30

函数指针 函数指针 函数指针 函数指针 应用应用

– 编写通用性更强的函数编写通用性更强的函数 典型实例典型实例

– 计算函数的定积分计算函数的定积分 另一个典型实例另一个典型实例

– 既能按照升序排序,又能按降序排序既能按照升序排序,又能按降序排序 见见《《 CC 语言大学实用教程学习指导语言大学实用教程学习指导》》 P385-395P385-395

Page 31: 第 9 章 函数的高级应用

23/4/20C语言大学实用教程课件 制作人 苏小红

31

函数指针 函数指针 函数指针 函数指针 void Sort(int a[], int n, int (*compare)(int a, int b)) void Sort(int a[], int n, int (*compare)(int a, int b)) {{ …… ……if ((*compare)(a[i],max) if ((*compare)(a[i],max)

…… ……..}}

/*/* 决定数据是否按升序排序决定数据是否按升序排序 ,a<b,a<b 为真为真 ,,则按升序排序则按升序排序 **//int Ascending(int a, int b)int Ascending(int a, int b){{ return a < b;return a < b;

}}/*/* 决定数据是否按降序排序决定数据是否按降序排序 ,a>b,a>b 为真为真 ,,则按降序排序则按降序排序 **//int Descending(int a, int b)int Descending(int a, int b){{ return a > b;return a > b;

} }

Page 32: 第 9 章 函数的高级应用

23/4/20C语言大学实用教程课件 制作人 苏小红

32

函数指针 函数指针 函数指针 函数指针 其他应用其他应用 函数指针通常用在菜单驱动的系统中函数指针通常用在菜单驱动的系统中

– 系统提示用户从菜单中选择一种操作(可能是系统提示用户从菜单中选择一种操作(可能是 11 ~~66 ))

– 对应于用户选择的不同操作是由不同的函数来完成的对应于用户选择的不同操作是由不同的函数来完成的– 先将指向每个函数的指针存储在一个指针数组中先将指向每个函数的指针存储在一个指针数组中– 调用函数完成相应操作时调用函数完成相应操作时

将用户输入的选择,作为该指针数组的下标将用户输入的选择,作为该指针数组的下标 利用存储于相应数组元素中的函数指针,调用相应的函数利用存储于相应数组元素中的函数指针,调用相应的函数

见见《《 CC 语言大学实用教程学习指导语言大学实用教程学习指导》》

Page 33: 第 9 章 函数的高级应用

23/4/20C语言大学实用教程课件 制作人 苏小红

33

作业作业作业作业 习题习题

– 9.1~9.39.1~9.3