<4D F736F F F696E74202D2043D3EFD1D4BFCEBCFE2D362DBAAFCAFD2E BBCE6C8DDC4A3CABD5D>

Similar documents
chap07.key

PowerPoint 演示文稿

《C语言程序设计》教材习题参考答案

Microsoft PowerPoint - 3. 函数Functionl.ppt [兼容模式]

PowerPoint 演示文稿

《C语言程序设计》教材习题参考答案

没有幻灯片标题

《C语言程序设计》第2版教材习题参考答案

网C试题(08上).doc

Microsoft PowerPoint - 01_Introduction.ppt

Microsoft Word - 第3章.doc

Microsoft PowerPoint - 4. 数组和字符串Arrays and Strings.ppt [兼容模式]

Microsoft Word - 把时间当作朋友(2011第3版)3.0.b.06.doc

Guava学习之Resources

Microsoft Word - 第05章 函数

Generated by Unregistered Batch DOC TO PDF Converter , please register! 浙江大学 C 程序设计及实验 试题卷 学年春季学期考试时间 : 2003 年 6 月 20 日上午 8:3

2013 C 1 # include <stdio.h> 2 int main ( void ) 3 { 4 int cases, a, b, i; 5 scanf ("%d", & cases ); 6 for (i = 0;i < cases ;i ++) 7 { 8 scanf ("%d %d

06-statement

主要内容 函数的定义 声明与调用 函数间的参数传递 函数嵌套与内联函数 形参带缺省值的函数与函数重载 数据的作用域 预编译处理与多文件结构 C++ 系统函数 2

CC213

C 1 # include <stdio.h> 2 int main ( void ) { 4 int cases, i; 5 long long a, b; 6 scanf ("%d", & cases ); 7 for (i = 0;i < cases ;i ++) 8 { 9

C/C++ - 函数

download.kaoyan.com_2006ÄêÌì½ò¹¤Òµ´óѧ¸ß¼¶ÓïÑÔ³ÌÐòÉè¼Æ£¨409£©¿¼ÑÐÊÔÌâ

C++ 程序设计 告别 OJ1 - 参考答案 MASTER 2019 年 5 月 3 日 1

C 1

Microsoft PowerPoint - 5. 指针Pointers.ppt [兼容模式]

untitled

C C

2015年计算机二级(C语言)模拟试题及答案(四)

运算符重载 为什么要 运算符重载 那些运算符可以重载, 哪些不可以 如何实现运算符重载 实现方式 : 成员函数与非成员函数 类型转换 怎样实现对象与基本数据类型数据的运算 2

C/C++语言 - C/C++数据

エスポラージュ株式会社 住所 : 東京都江東区大島 東急ドエルアルス大島 HP: ******************* * 关于 Java 测试试题 ******

NOWOER.OM m/n m/=n m/n m%=n m%n m%=n m%n m/=n 4. enum string x1, x2, x3=10, x4, x5, x; 函数外部问 x 等于什么? 随机值 5. unsigned char *p1; unsigned long *p

C/C++程序设计 - 字符串与格式化输入/输出

第5章 递归 (Recurve)

untitled

试卷代号 :1075 座位号 rn 国家开放大学 ( 中央广播电视大学 )2015 年秋季学期 " 开放本科 " 期末考试 c+ 十语言程序设计试题 2016 年 1 月 t 问一 Urr-f 斗 士 1 1. 下面的保留字 ( ) 不能作为函数的返回类型 A. void B. int C. new

2015年计算机二级(C语言)模拟试题及答案(三)

C/C++语言 - 运算符、表达式和语句

C

试卷代号 :1253 座位号 E 口 国家开放大学 ( 中央广播电视大学 )2014 年秋季学期 " 开放本科 " 期末考试 C 语言程序设计 A 试题 2015 年 1 月 E 四! 五 总分! 一 单选题 ( 每小题 2 分, 共 20 分 ) 1. 由 C 语言源程序文件编译而成的目标文件的默

新・解きながら学ぶC言語

试卷代号 ~1075 座位号 E 口 国家开放大学 ( 中央广播电视大学 )20]5 年秋季学期 " 开放本科 " 期末考试 C 十十语言程序设计 试题 同二二十斗 2016 年 1 月 巴叫一 1. 下面的保留字 ( ) 不能作为函数的返回类型 A. void B. int C. new D. l

C/C++ - 文件IO

Microsoft PowerPoint - w10.ppt

新版 明解C言語入門編

山东师大数学科学学院

OOP with Java 通知 Project 4: 4 月 18 日晚 9 点 关于抄袭 没有分数

C/C++ 语言 - 循环

FY.DOC

新・明解C言語入門編『索引』

Microsoft Word - 第3章.doc

第五讲 数组 一维数组 二维数组 数组作为函数参数

untitled

第三章 函数

第5章 递归 (Recurve)

Static Enforcement of Security with Types

nooog

Microsoft PowerPoint - 8. 运算符重载 Operator Overloading.pptx

C++ 程序设计 告别 OJ2 - 参考答案 MASTER 2019 年 5 月 3 日 1

C

第一章三角函数 1.3 三角函数的诱导公式 A 组 ( ) 一 选择题 : 共 6 小题 1 ( 易诱导公式 ) 若 A B C 分别为 ABC 的内角, 则下列关系中正确的是 A. sin( A B) sin C C. tan( A B) tan C 2 ( 中诱导公式 ) ( ) B. cos(

C/C++ - 字符输入输出和字符确认

Microsoft Word - 综合试题2.doc

技能竞赛C试题

untitled

移动平台应用软件开发 C/C++/JAVA 基础 C 中的预处理指令 主讲 : 张齐勋 移动平台应用软件开发 课程建设小组北京大学二零一五年

ARM中C和汇编混合编程及示例.doc

c_cpp

帝国CMS下在PHP文件中调用数据库类执行SQL语句实例

2013 C 1 #include <stdio.h> 2 int main(void) 3 { 4 int cases, i; 5 long long a, b; 6 scanf("%d", &cases); 7 for (i = 0; i < cases; i++) 8 { 9 scanf("%

Microsoft Word - 《C语言开发入门》课程教学大纲-2.doc

上海市本科教学质量年度报告

Microsoft Word 年9月二级C真卷.doc

untitled

R 软件介绍 (3): R编程基础

Microsoft Word - C++CodeRule94.doc

立 志 于 打 造 最 贴 近 考 生 实 际 的 辅 导 书 计 算 机 考 研 之 数 据 结 构 高 分 笔 记 率 辉 编 著 周 伟 张 浩 审 核 讨 论 群 :

untitled

R 软件介绍 (3): R编程基础

幻灯片 1

一二三四五六七八九十一二三四五六七八九十一二三四五六七八九十一二三四五六七八二三四五六七八九十一二三四五六七八九十一二三四五六七八九十一二三四五六七八二三四五六七八九十一二三四五六七八九十一二三四五六七八九十一二三四五六七八二三四五六七八九十一二三四五

CC213

PowerPoint 演示文稿

Microsoft PowerPoint - 10 模板 Template.pptx

第10章 函数与程序结构

untitled

<4D F736F F F696E74202D BDE1B9B9BBAFB3CCD0F2C9E8BCC D20D1ADBBB7>

第2章 递归与分治策略

Microsoft PowerPoint - 0 C复习.ppt [兼容模式]

untitled

<4D F736F F D205A572D2D A1AAA1AAD4ACE7F42D43D3EFD1D4CAB5D1B5BDCCB3CC2E646F6378>

OOP with Java 通知 Project 4: 4 月 19 日晚 9 点

期中考试试题讲解

2001年(下)局域网技术与组网工程试卷答案

第3章.doc

C 语言程序设计 ( 第 33 版 )) #define PI // 下面 2 行是函数的原型说明部分 double sup_area(double r); double volume(double r); // 下面是主函数部分 { double a=-5,b,c,d; b=fab

Microsoft Word - 第3章.doc

epub 33-8

6 C51 ANSI C Turbo C C51 Turbo C C51 C51 C51 C51 C51 C51 C51 C51 C C C51 C51 ANSI C MCS-51 C51 ANSI C C C51 bit Byte bit sbit

Transcription:

第六章函数 郎大鹏

第六章函数 6.1 高效程序的编写方法 6.2 函数的定义 6.3 函数间数据的传递方法 6.4 函数的调用 6.5 函数的嵌套调用 6.6 函数的递归调用 6.7 局部变量和全局变量 6.8 变量的存储类别 6.9 习题

第六章函数 6.1 高效程序的编写方法 6.2 函数的定义 6.3 函数间数据的传递方法 6.4 函数的调用 6.5 函数的嵌套调用 6.6 函数的递归调用 6.7 局部变量和全局变量 6.8 变量的存储类别 6.9 习题

6.1 高效程序的编写方法 程序员在设计一个复杂的应用程序时, 往往也是把整个程序划分为若干功能较为单一的程序模块, 然后分别予以实现, 最后再把所有的程序模块像搭积木一样装配起来, 这种在程序设计中分而治之的策略, 被称为模块化程序设计方法 函数是程序的基本组成单位, 因此可以很方便地用函数作为程序模块来实现 C 语言程序 利用函数, 不仅可以实现程序的模块化, 程序设计得简单和直观, 提高了程序的易读性和可维护性,

6.1 高效程序的编写方法 由于采用了函数模块式的结构,C 语言易于实现结构化程序设计 使程序的层次结构清晰, 便于程序的编写 阅读 调试 例 6.1 输出两行 * 和一行信息, 如下所示 : #include<stdio.h> void main() { void star(); /* 对 star 函数进行声明 */ void message(); /* 对 message 函数进行声明 */ star(); /* 调用 star 函数 */ message(); /* 调用 message 函数 */ star(); /* 调用 star 函数 */ void star() /* 定义 star 函数 */ { printf(" **********\n"); void message() /* 定义 message 函数 */ { printf(" welcome\n");

6.1 高效程序的编写方法 1. 一个源程序文件由一个或多个函数组成 一个源程序文件以源文件为单位进行编译, 而不是以函数为单位进行编译 2. 一个 C 程序由一个或多个源程序文件组成 对较大的程序, 一般不希望全放在一个文件中, 而将函数和其它内容 ( 如预定义 ) 分别放到若干个源文件中, 再由若干源文件组成一个 C 程序 3.C 程序的执行从 main 函数开始, 调用其它函数后流程回到 main 函数, 在 main 函数中结束整个程序的运行 main 函数是系统定义的 4. 所有函数都是平行的, 一个函数并不从属于另一函数, 即函数不能嵌套定义 ( 这是和 PASCAL 不同的 ), 但可以互相调用, 但不能调用 main 函数

6.1 高效程序的编写方法 5. 从用户使用的角度看, 函数有两种 : (1) 标准函数, 即库函数 这是由系统提供的, 用户不必自己定义这些函数, 可以直接使用它们 应该说明, 每个系统提供的库函数的数量和功能不同, 当然有一些基本的函数是共同的 (2) 用户自己定义的函数, 以解决用户的专门需要 6. 从函数的形式看, 函数分两类 (1) 无参函数 如例 6.1 中的 star 和 message 就是无参函数 在调用无参函数时, 主调函数并不将数据传送给被调用函数, 一般用来执行指定的一组操作 ( 例如例 6.1 那样, 以 star 函数的作用就是输出 18 个星号 ) 无参函数可以带回或不带回函数值, 但一般以不带回函数值的居多 (2) 有参函数 在调用函数时, 在主调函数和被调用函数之间有参数传递, 也就是说, 主调函数可以将数据传给被调用函数使用, 被调用函数中的数据也可以带回来供主调函数使用

第六章函数 6.1 高效程序的编写方法 6.2 函数的定义 6.3 函数间数据的传递方法 6.4 函数的调用 6.5 函数的嵌套调用 6.6 函数的递归调用 6.7 局部变量和全局变量 6.8 变量的存储类别 6.9 习题

6.2 函数的定义 6.2.2 无参函数的定义 类型标识符函数名 () { 说明部分语句 void Hello() { printf ("Hello,world \n"); 函数的类型实际上是函数返回值的类型 函数名是由用户定义的标识符 函数名后有一个空括号, 其中无参数, 但括号不可少 { 中的内容称为函数体 在函数体中声明部分, 是对函数体内部所用到的变量的类型说明 在很多情况下都不要求无参函数有返回值, 此时函数类型符可以写为 void

6.2 函数的定义 6.2.2 有参函数的定义 类型标识符函数名 ( 形式参数表列 ) { 说明部分 语句 int max(int a, int b) { if (a>b) return a; else return b; 第一行说明 max 函数是一个整型函数, 其返回的函数值是一个整数 形参为 a,b, 均为整型量 a,b 的具体值是由主调函数在调用时传送过来的 在 { 中的函数体内, 除形参外没有使用其它变量, 因此只有语句而没有声明部分 在 max 函数体中的 return 语句是把 a( 或 b) 的值作为函数的值返回给主调函数 有返回值的函数中至少应有一个 return 语句

6.2 函数的定义 6.2.2 有参函数的定义 在 C 程序中, 一个函数的定义可以放在任意位置, 既可放在主函数 main 之前, 也可放在 main 之后 include <stdio.h> void main() { int max(int a,int b); int x,y,z; printf("input two numbers:\n"); scanf("%d%d",&x,&y); z=max(x,y); printf("maxmum=%d",z); int max(int a,int b) { if(a>b)return a; else return b;

6.2 函数的定义 空函数 什么工作也不做, 没有任何实际作用 在主调函数中写上 fun1() 表明 这里要调用一个函数, 而现在这个函数没有起作用, 等以后扩充函数功能时补充上 C 语言可以有 空函数, 它的形式为 : 类型说明符函数名 () { 例如 fun1( ) {

第六章函数 6.1 高效程序的编写方法 6.2 函数的定义 6.3 函数间数据的传递方法 6.4 函数的调用 6.5 函数的嵌套调用 6.6 函数的递归调用 6.7 局部变量和全局变量 6.8 变量的存储类别 6.9 习题

6.3 函数间数据的传递方法 6.3.1 形式参数和实际参数 形参和实参的功能是作数据传送 发生函数调用时, 主调函数把实参的值传送给被调函数的形参从而实现主调函数向被调函数的数据传送 1 形参变量只有在被调用时才分配内存单元, 在调用结束时, 即刻释放所分配的内存单元 2 实参在使用时, 应预先用赋值, 输入等办法使实参获得确定值 3 实参和形参在数量上, 类型上, 顺序上应严格一致, 否则会发生类型不匹配的错误 4 函数调用中发生的数据传送是单向的 即只能把实参的值传送给形参, 而不能把形参的值反向地传送给实参

6.3 函数间数据的传递方法 例 6.3 计算两个整数的平均数的函数 #include<stdio.h> int average(int x,int y) { /* 函数定义 */ int z ; z=(x+y)/2 ; return(z) ; void main(){ int a=36; int b=22; int c=average(a,b); /* 函数调用 */ printf("average of %d and %d is %d.\n",a,b,c);

6.3 函数间数据的传递方法 6.3.1 形式参数和实际参数 说明 : average 函数通过参数传递知道了要计算的是 36 和 22 的平均值 在进行函数调用时, 把变量 a 和 b 的值作为参数提供给 average 函数 这时程序开始执行 average 函数, 并把 a 和 b 的值分别传递给 average 函数中定义的参数 x 与 y, 这个过程就是参数传递 函数内接收数据的参数叫形式参数, 简称形参, 调用函数提供的参数叫实际参数, 简称实参 本例中的 x 和 y 就是形参,a 和 b 是实参

6.3 函数间数据的传递方法 上述调用过程可以用下图说明

6.3 函数间数据的传递方法 6.3.2 函数的返回值 通常, 希望通过函数调用使主调函数能得到一个确定的值, 这就是函数的值, 也就是函数的返回值 1. 函数的返回值是通过函数中的 return 语句获得的 return 语句将被调用函数中的一个确定值带回主调函数中去 如果不需要从被调用函数带回函数值可以不要 return 语句 return 后面的值可以是一个表达式 例如, 例 6.2 中的函数 max 可以改写如下 : int max(int a,int b) { return(x>y?x: y);

6.3 函数间数据的传递方法 6.3.2 函数的返回值 2. 函数值的类型 既然函数有返回值, 这个值当然应属于某一个确定的类型, 应当在定义函数时指定函数值的类型 例如 : int max(int x,int y) 函数值为整型 char letter(char c1,char c2) 函数值为字符型 double min(double x,double y) 函数值为双精度型 3. 如果函数值的类型和 return 语句中表达式的值不一致, 则以函数值的类型为准 对数值型数据, 可以自动进行类型转换 即函数类型决定返回值的类型

6.3 函数间数据的传递方法 例 6.4 将例 6.2 稍作改动 ( 注意是变量的类型改动 ) #include <stdio.h> void main() { float a,b; int c; scanf( %f,%f,&a,&b); c=max(a,b); printf( Max is %d,c); int max(float x, float y); { float z ; z = x > y? x :y ; return(z); 运行情况如下 : 1.5,2.5 Max is 2

6.3 函数间数据的传递方法 4. 如果被调用函数中没有 return 语句, 并不带回一个确定的 用户所希望得到的函数值, 但实际上, 函数并不是不带回值, 而只是不带回有用的值, 带回的是一个不确定的值 { int a,b,c; a=star() ; b=message(); c=star(); printf(:%d,%d,%d,a,b,c); 运行时除得到和例 6.1 一样的结果外, 还可以输出 a,b, c 值 当然,a,b,c 的值不一定有实际意义.

6.3 函数间数据的传递方法 5. 为了明确表示 不带回值, 可以用 void 定义 无类型 ( 或称 空类型 ), 例如, 例 6.1 中的定义为 : void star() { void message() { 这样, 系统就保证不使函数带回任何值, 即禁止在调用函数中使用被调用函数的返回值 如果 star 和 messag 函数定义为 void 型, 则下面的用法就是错误的 :a=star( ) ; b=message( );

第六章函数 6.1 高效程序的编写方法 6.2 函数的定义 6.3 函数间数据的传递方法 6.4 函数的调用 6.5 函数的嵌套调用 6.6 函数的递归调用 6.7 局部变量和全局变量 6.8 变量的存储类别 6.9 习题

6.4 函数的调用 6.4.1 函数调用的一般形式 C 语言中, 函数调用的一般形式为 : 函数名 ( 实参表列 ); 如果是调用无参函数则实参表列可以没有, 但括弧不能省略 在 C 语言中, 可以用以下几种方式调用函数 : 1 函数表达式 : 函数作为表达式中的一项出现在表达式中, 以函数返回值参与表达式的运算 这种方式要求函数是有返回值的 2 函数语句 : 函数调用的一般形式加上分号即构成函数语句 3 函数实参 : 函数作为另一个函数调用的实际参数出现 这种情况是把该函数的返回值作为实参进行传送, 因此要求该函数必须是有返回值的

6.4 函数的调用 在函数调用中还应该注意的一个问题是求值顺序的问题 所谓求值顺序是指对实参表中各量是自左至右使用呢, 还是自右至左使用 例 6.5 #include <stdio.h> int f(int a,int b); { int c; if(a>b)c=1; else if(a==b)c=0; else c =-1; return(c); void main( ) { int i=2,p; p=f(i,++i) printf( %d,p);

6.4 函数的调用 如果本意是按自左而右顺序求实参的值的, 可以改写为 : j=i; k=++i; p=f(j,k); 如果本意是自右而左求实参的值的, 可改写为 : j=++i; p=f(i,j) 这种情况在 printf 函数中也同样存在, 如 printf( %d,%d,i,i++);

6.4 函数的调用 6.4.1 函数的声明和函数原型 在主调函数中调用某函数之前应对该被调函数进行说明 ( 声明 ), 函数声明的形式为 : 类型说明符函数名 ( 类型形参, 类型形参 ); 或为 : 类型说明符函数名 ( 类型, 类型 ); 括号内给出了形参的类型和形参名, 或只给出形参类型 这便于编译系统进行检错, 以防止可能出现的错误

6.4 函数的调用 6.4.1 函数的声明和函数原型 例 6.2 main 函数中对 max 函数的说明为 : int max(int a,int b); 或写为 : int max(int,int); 1 如果被调函数的返回值是整型或字符型时, 可以不对被调函数作说明, 而直接调用 这时系统将自动对被调函数返回值按整型处理 2 当被调函数的函数定义出现在主调函数之前时, 在主调函数中也可以不对被调函数再作说明而直接调用

6.4 函数的调用 如在所有函数定义之前, 在函数外预先说明了各个函数的类型, 则在以后的各主调函数中, 可不再对被调函数作说明 例如 : char str(int a); float f(float b); main() { char str(int a) { float f(float b) { 其中第一, 二行对 str 函数和 f 函数预先作了说明 因此在以后各函数中无须对 str 和 f 函数再作说明就可直接调用

第六章函数 6.1 高效程序的编写方法 6.2 函数的定义 6.3 函数间数据的传递方法 6.4 函数的调用 6.5 函数的嵌套调用 6.6 函数的递归调用 6.7 局部变量和全局变量 6.8 变量的存储类别 6.9 习题

6.5 函数的嵌套调用 C 语言的函数定义都是互相平行 独立的, 也就是说在定义函数时, 一个函数内不能包含另一个函数 即 C 语言不能嵌套定义函数, 但可以嵌套调用函数, 也就是说, 在调用一个函数的过程中又调用另一个函数

6.5 函数的嵌套调用 例 6.6 计算 (1!) 2 +(2!) 2 +(3!) 2 #include<stdio.h> long f1(int p) { /* 计算 1 到 3 阶乘的 2 次方 */ long l; long f2(int); /* 函数声明 */ l=f2(p); /* 对函数 f2 的调用 */ return l*l; long f2(int q) { /* 计算 1 到 3 的阶乘 */ long c=1; int i; for(i=1;i<=q;i++) c=c*i; return(c); void main(){ int i; long s=0; for (i=1;i<=3;i++) s=s+f1(i); /* 对函数 f1 的调用 */ printf("\ns=%ld\n",s);

6.5 函数的嵌套调用 例 6.7 计算 s=1 k +2 k +3 k + +N k /* 功能 : 函数的嵌套调用 */ #define K 4 #define N 5 long f1(int n,int k) { /* 计算 n 的 k 次方 */ long power=n; int i; for(i=1;i<k;i++) power *= n; return power; long f2(int n,int k){ /* 计算 1 到 n 的 k 次方之累加和 */ long sum=0; int i; for(i=1;i<=n;i++) sum += f1(i, k); /* 以 i k 作实参, 循环调用函数 f1*/ return sum; void main() { printf("sum of %d powers of integers from 1 to %d = ",K,N); printf("%d\n",f2(n,k)); /* 以 N K 作实参, 调用函数 f2*/

第六章函数 6.1 高效程序的编写方法 6.2 函数的定义 6.3 函数间数据的传递方法 6.4 函数的调用 6.5 函数的嵌套调用 6.6 函数的递归调用 6.7 局部变量和全局变量 6.8 变量的存储类别 6.9 习题

形参 && 实参 函数的形参和实参具有以下特点 : 1. 形参变量只有在被调用时才分配内存单元, 在调用结束时, 即刻释放所分配的内存单元 因此, 形参只有在函数内部有效 函数调用结束返回主调函数后则不能再使用该形参变量 2. 实参可以是常量 变量 表达式 函数等, 无论实参是何种类型的量, 在进行函数调用时, 它们都必须具有确定的值, 以便把这些值传送给形参 因此应预先用赋值, 输入等办法使实参获得确定值 3. 实参和形参在数量上, 类型上, 顺序上应严格一致, 否则会发生类型不匹配 的错误 4. 函数调用中发生的数据传送是单向的 即只能把实参的值传送给形参, 而不能把形参的值反向地传送给实参 因此在函数调用过程中, 形参的值发生改变, 而实参中的值不会变化

printf( 例子 \n ); #include <stdio.h> main() { int n; printf("input number\n"); scanf("%d",&n); s(n); printf("n=%d\n",n); int s(int n) { int i; for(i=n-1;i>=1;i--) n=n+i; printf("n=%d\n",n);

计算机的基础知识 约翰 冯 诺依曼计算机之父

printf( 例子 \n ); #include<stdio.h> void fun(int k); void main() { int w=5; fun(w); void fun(int k) { if(k>0) fun(k-1); printf("%d",k);

printf( 例子 \n );

6.6 函数的递归调用 函数的递归调用是指, 一个函数在它的函数体内, 直接或间接地调用它自身 这种函数称为递归函数 C 语言允许函数的递归调用 在递归调用中, 调用函数又是被调用函数, 执行递归函数将反复调用其自身 每调用一次就进入新的一层

6.6 函数的递归调用 为了防止递归调用无终止地进行, 必须在函数内有终止递归调用的手段 常用的办法是加条件判断, 满足某种条件后就不再作递归调用, 然后逐层返回 一个递归的过程可以分为 回推 和 递推 两个阶段 第一阶段是 回推, 即将未知逐层回推到已知, 然后开始第二阶段, 采用递推方法, 从已知推算出未知, 一直推算出问题解为止 下面用一个通俗的例子来说明 例 6.8 有 6 个坐在一桌, 问第 6 个人多少岁?, 他说比第 5 个人大 2 岁 问第 5 个人多少岁? 他说比第 4 个人大 2 岁 问第 4 个人多少岁? 他说比第 3 个人大 2 岁 问第 3 个人多少岁? 他说比第 2 个人大 2 岁 问第 2 个人多少岁? 他说比第 1 个人大 2 岁 最后问第 1 个人, 他说他 6 岁 请问第 6 个人多大?

6.6 函数的递归调用 显然, 这是一个递归问题 依题意 : age(6)=age(5)+2 age(5)=age(4)+2 age(4)=age(3)+2 age(3)=age(2)+2 age(2)=age(1)+2 age(1)=6 用数学公式来表示 : age(n)= 6 (n=1) age(n-1)+2 (n>1) 用递归解决此问题的程序如下 : #include<stdio.h> int age(int a) { int c; if( n = = 1) c=6; else c=age(n-1)+2; return(c); void main() { printf("%d\n",age(6));

6.6 函数的递归调用

6.6 函数的递归调用 例 6.9 用递归法计算 n! 用递归法计算 n!, 可用下述公式表示 : n!=1 (n=0,1) n (n-1)! (n>1) 按公式可编程如下 : #include <stdio.h> long ff(int n){ long f; if(n<0) printf("n<0,input error"); else if(n==0 n==1) f=1; else f=ff(n-1)*n; return(f); void main(){ int n; long y; printf("\n input a inteager number:\n"); scanf("%d",&n); y=ff(n); printf("%d!=%ld",n,y); 程序中给出的函数 ff 是一个递归函 数 主函数调用 ff 后即进入函数 ff 执行, 如果 n<0,n==0 或 n=1 时都将 结束函数的执行, 否则就递归调 用 ff 函数自身 由于每次递归调用的实参为 n-1, 即把 n-1 的值赋予形参 n, 最后当 n- 1 的值为 1 时再作递归调用, 形参 n 的值也为 1, 将使递归终止 然后 可逐层退回

6.6 函数的递归调用 例 6.10 Hanoi 塔问题汉诺塔问题是现代递归算法思想的来源, 是一个很经典的问题 汉诺塔问题是印度的一个古老的传说 开天辟地的神勃拉玛在一个庙里留下了三根金刚石的棒, 第一根上面套着 64 个圆的金片, 最大的一个在底下, 其余一个比一个小, 依次叠上去, 庙里的众僧不倦地把它们一个个地从这根棒搬到另一根棒上, 规定可利用中间的一根棒作为帮助, 但每次只能搬一个, 而且大的不能放在小的上面 为方便, 我们给这三根棒命名为 A,B,C A 棒上套有 64 个大小不等的圆盘, 大的在下, 小的在上 如图 6.11 所示 要把这 64 个圆盘从 A 棒移动 C 棒上, 每次只能移动一个圆盘, 移动可以借助 B 棒进行 但在任何时候, 任何棒上的圆盘都必须保持大盘在下, 小盘在上 求移动的步骤

6.6 函数的递归调用 本题算法分析如下, 设 A 上有 n 个盘子 如果 n=1, 则将圆盘从 A 直接移动到 C 如果 n=2, 则 : 1. 将 A 上的 n-1( 等于 1) 个圆盘移到 B 上 ; 2. 再将 A 上的一个圆盘移到 C 上 ; 3. 最后将 B 上的 n-1( 等于 1) 个圆盘移到 C 上 如果 n=3, 则 : A. 将 A 上的 n-1( 等于 2, 令其为 n`) 个圆盘移到 B( 借助于 C), 步骤如下 : (1) 将 A 上的 n`-1( 等于 1) 个圆盘移到 C 上 (2) 将 A 上的一个圆盘移到 B (3) 将 C 上的 n`-1( 等于 1) 个圆盘移到 B B. 将 A 上的一个圆盘移到 C C. 将 B 上的 n-1( 等于 2, 令其为 n`) 个圆盘移到 C( 借助 A), 步骤如下 : (1) 将 B 上的 n`-1( 等于 1) 个圆盘移到 A (2) 将 B 上的一个盘子移到 C (3) 将 A 上的 n`-1( 等于 1) 个圆盘移到 C

6.6 函数的递归调用 到此, 完成了三个圆盘的移动过程 从上面分析可以看出, 当 n 大于等于 2 时, 移动的过程可分解为三个步骤 : 第一步把 A 上的 n-1 个圆盘移到 B 上 ; 第二步把 A 上的一个圆盘移到 C 上 ; 第三步把 B 上的 n-1 个圆盘移到 C 上 ; 其中第一步和第三步是类同的 当 n=3 时, 第一步和第三步又分解为类同的三步, 即把 n`-1 个圆盘从一个棒移到另一个棒上, 这里的 n`=n-1 显然这是一个递归过程

6.6 函数的递归调用 #include <stdio.h> void move(int n,int x,int y,int z) { if(n==1) printf("%c-->%c\n",x,z); else { move(n-1,x,z,y); printf("%c-->%c\n",x,z); move(n-1,y,x,z); void main() { int h; printf("\ninput number:\n"); scanf("%d",&h); printf("the step to moving %2d diskes:\n",h); move(h,'a','b','c'); move 函数是一个递归函数, 它有四个形参 n,x,y,z n 表示圆盘数,x,y,z 分别表示三根棒 move 函数的功能是把 x 上的 n 个圆盘移动到 z 上 如 n!=1 则分为三步 : 递归调用 move 函数, 把 n- 1 个圆盘从 x 移到 y; 输出 x z; 递归调用 move 函数, 把 n-1 个圆盘从 y 移到 z 在递归调用过程 中 n=n-1, 故 n 的值逐次递减, 最后 n=1 时, 终 止递归, 逐层返回

第六章函数 6.1 高效程序的编写方法 6.2 函数的定义 6.3 函数间数据的传递方法 6.4 函数的调用 6.5 函数的嵌套调用 6.6 函数的递归调用 6.7 局部变量和全局变量 6.8 变量的存储类别 6.9 习题

6.7 局部变量和全局变量 在讨论函数的形参变量时曾经提到, 形参变量只在被调用期间才分配内存单元, 调用结束立即释放 这一点表明形参变量只有在函数内才是有效的, 离开该函数就不能再使用了 这种变量有效性的范围称变量的作用域 不仅对于形参变量,C 语言中所有的量都有自己的作用域 变量说明的方式不同, 其作用域也不同 C 语言中的变量, 按作用域范围可分为两种, 即局部变量和全局变量

6.7 局部变量和全局变量 在一个函数内部定义的变量是内部变量, 内部变量只在本函数范围的内部有效, 只能在本函数的内部使用它们, 在此函数之外就不能使用这些变量了 所以内部变量也称 局部变量

6.7 局部变量和全局变量 关于局部变量的作用域说明以下几点 : ⑴ 主函数 main() 中定义的局部变量, 也只能在主函数中使用, 其它函数不能使用 同时, 主函数中也不能使用其它函数中定义的局部变量 因为主函数也是一个函数, 与其它函数是平行关系 这一点是与其它语言不同的, 应予以注意 ⑵ 形参变量也是局部变量, 属于被调用函数 ; 实参变量, 则是调用函数的局部变量 ⑶ 允许在不同的函数中使用相同的变量名, 它们代表不同的对象, 分配不同的单元, 互不干扰, 也不会发生混淆 ⑷ 在一个函数内部, 在复合语句中也可以定义变量, 这些变量只在本复合语句中有效

6.7 局部变量和全局变量 例 6.11 #include <stdio.h> void main() {int a=1; {int a=2; printf("in the inner block,a=%d\n",a); printf ("In the outer block,a=%d\n",a); 变量的作用域规则是 : 每个变量仅在定义它的复合语句 ( 包含下级复合语句 ) 内有效, 并且拥有自己的内存空间 可见, 在内层复合语句使用的变量是它自己的 a, 而非外 层定义的 a, 注意, 这两个 a 是真正的两个 a, 各有自己的 存储空间, 彼此毫无关系

6.7 局部变量和全局变量 6.7.2 全局变量 全局变量也称为外部变量, 它是在函数外部定义的变量 它不属于哪一个函数, 它属于一个源程序文件 其作用域是整个源程序 在函数中使用全局变量, 一般应作全局变量说明 只有在函数内经过说明的全局变量才能使用 如下 :

6.7 局部变量和全局变量

6.7 局部变量和全局变量 对于全局变量的几点说明 : ⑴ 全局变量可加强函数模块之间的数据联系, 但又使这些函数依赖这些全局变量, 如果在一个函数中改变了全局变量的值, 就能影响到其他函数, 因而使得这些函数的独立性降低 从模块化程序设计的观点来看这是不利的, 因此不是非用不可时, 不要使用外部变量 ⑵ 在同一源文件中, 允许全局变量和局部变量同名 在局部变量的作用域内, 全局变量将被屏蔽而不起作用 ⑶ 全局变量的作用域是从定义点到本文件结束

6.7 局部变量和全局变量 例 6.12 外部变量与局部变量同名 #include <stdio.h> int a=5,b=8; /* 定义全局变量 */ int max(int a,int b) /* 定义函数, 其中 a,b 是形参 */ { /* 形参 a,b 是局部变量与全局变量同名 */ int c; c=a>b?a:b; return(c); void main() { int a=12; /* 局部变量 a 与全局变量同名 */ printf("max=%d",max(a,b));

第六章函数 6.1 高效程序的编写方法 6.2 函数的定义 6.3 函数间数据的传递方法 6.4 函数的调用 6.5 函数的嵌套调用 6.6 函数的递归调用 6.7 局部变量和全局变量 6.8 变量的存储类别 6.9 习题

6.8 存储变量的类别 6.8.1 动态存储方式与静态存储方式 从变量的作用域 ( 即从空间 ) 角度来分, 可以分为全局变量和局部变量 从变量值存在的作时间 ( 即生存期 ) 角度来分, 可以分为静态存储方式和动态存储方式 静态存储方式 : 是指在程序运行期间分配固定的存储空间的方式 动态存储方式 : 是在程序运行期间根据需要进行动态的分配存储空间的方式 程序运行时的内存分配情况, 如右图 : 1. 系统区 2. 程序代码区 3. 静态存储区 4. 动态存储区 5. 自由空间 系统区程序代码区静态存储区动态存储区自由空间 静态的局部变量 全局变量 自动局部变量 ; 形参变量 ; 终端的现场保护

6.8 存储变量的类别 6.8.2 auto 变量

6.8 存储变量的类别 6.8.3 用 statice 声明局部变量

6.8 存储变量的类别 6.8.4 register 变量

6.8 存储变量的类别 6.8.4 extern 声明外部变量

6.8 存储变量的类别

第六章函数 6.1 高效程序的编写方法 6.2 函数的定义 6.3 函数间数据的传递方法 6.4 函数的调用 6.5 函数的嵌套调用 6.6 函数的递归调用 6.7 局部变量和全局变量 6.8 变量的存储类别 6.9 习题

6.9 习题