C语言-函数-null第四讲 函数Page *第四讲 函数参考书
《计算机程序设计基础》
第一版第5章5.3-5.5,第6章,第7章7.1-7.2,第8章8.2.4,8.2.6,第12章;
第二版第3章,第4章4.2、4.4
《C程序设计》第七章本讲主要内容Page *本讲主要内容算法的表示
结构化程序的组织
函数的定义
函数的调用
递归函数
函数间的数据传递
变量的存储类型
变量的作用范围
内部函数和外部函数
标准函数库算法及其表示方法概要Page *算法及其表示方法概要程序的辨证统一:数据与代码
数据表示:指定程序使用的...
null第四讲 函数Page *第四讲 函数参考书
《计算机程序
基础》
第一版第5章5.3-5.5,第6章,第7章7.1-7.2,第8章8.2.4,8.2.6,第12章;
第二版第3章,第4章4.2、4.4
《C程序设计》第七章本讲主要内容Page *本讲主要内容算法的表示
结构化程序的组织
函数的定义
函数的调用
递归函数
函数间的数据传递
变量的存储类型
变量的作用范围
内部函数和外部函数
标准函数库算法及其表示方法概要Page *算法及其表示方法概要程序的辨证统一:数据与代码
数据表示:指定程序使用的数据结构与组织形式
代码组织:数据上所进行操作的描述与组织形式
算法的基本概念
为解决某类问题而设计或采取的方法或步骤
算法必须能够转化为计算机可执行的指令序列(代码)
算法基本特征:有穷性、确定性、输入、输出与有效性
代码与伪代码
均可以用来表达算法设计思想与算法执行步骤代码与伪代码Page *代码与伪代码给定两个正整数m与n,设计求解最大公因子的算法int gcd( int m, int n )
{
int r;
start:
r = m % n;
if( r == 0 )
return n;
m = n;
n = r;
goto start;
}代码以计算机语言书写,与程序语言相关,涉及到数据定义等细节伪代码界于自然语言与计算机语言之间,一般用符号或文字表示算法的实际执行步骤,不与特定的编程语言相关,不涉及数据定义等细节输入:整数m与n
输出:m与n的最大公因子r
步骤1:m除以n,余数为r
步骤2:若r为0,则n为所求,算法终止;否则
步骤3:将n作为新m,r作为新n,返回第1步重新计算结构化程序的组织Page *结构化程序的组织程序的结构化
程序的一般结构
结构化与函数抽象
程序范型程序的结构化Page *程序的结构化结构化语句:满足单入口单出口条件的语句
复合语句、分支语句与循环语句都是结构化语句
结构化程序:使用结构化语句设计的程序
结构定理:所有程序都可使用上述三类结构化语句实现
结构化优点:单入口单出口的控制流易于确定程序动态计算过程,易于理解程序的一般结构Page *#include
/* 包含必要的头文件 */
#define PI 3.14159265 /* PI宏定义, 一次定义多次使用 */
float radius, height, volume; /* 全局变量声明 */
void main() /* 主函数 */
{
/* 输入半径与高度 */
printf(“This program computes the volume of the cylinder.\n“);
printf(“Please input the radius value: “);
scanf(“%f“, &radius);
printf(“Please input the height value: “);
scanf(“%f“, &height);
/* 计算体积 */
volume = PI * radius * radius * height;
/* 输出体积 */
printf(“The volume of the cylinder is %f\n“, volume);
}程序的一般结构根据用户输入的底面半径与高度,计算圆柱体体积主函数包括输入、计算与输出三部分程序无非是对特定输入数据进行处理并输出处理结果的指令序列,所以任何程序都应包括输入、计算与输出三部分结构化与函数抽象Page *结构化与函数抽象程序设计过程
按照功能需求,进行自顶向下的功能分解与逐步求精,最终形成代码
大多数问题的求解过程非常复杂,如何合理地控制程序规模和复杂性呢?
程序的分割与结构化:着重于安排操作序列而不是数据结构,使程序易于创建、理解与维护
函数抽象:结构化程序设计的主要工具
体现要执行的命令、计算或任务,这些抽象构成了函数
用户只关心抽象的语法和该抽象提供的功能或服务,不关心如何实现该功能结构化与函数抽象示例Page *#include /* 包含必要的头文件 */
#define PI 3.14159265 /* PI宏定义, 一次定义多次使用 */
float radius, height, volume; /* 全局变量声明 */
void Input(); /* 输入半径与高度,将实际的输入操作隐藏在函数内部 */
void Compute(); /* 计算体积,将实际的计算过程隐藏在函数内部 */
void Output(); /* 输出体积,将实际的输出操作隐藏在函数内部 */
void main() /* 主函数,表现为对上述函数的调用,无其他代码 */
{
Input();
Compute();
Output();
}结构化与函数抽象示例根据用户输入的底面半径与高度,计算圆柱体体积主函数是否更容易理解?没有复杂的输入、计算与输出的实现细节,理解主函数一点都不困难在主函数层次,只需了解一旦声明三个全局变量,连续调用上述三个函数即能完成主函数的计算任务就可以了结构化与函数抽象示例(续)Page *结构化与函数抽象示例(续)void Input()
{
printf(“This program computes the volume of the cylinder.\n“);
printf(“Please input the radius value: “);
scanf(“%f“, &radius);
printf(“Please input the height value: “);
scanf(“%f“, &height);
}
void Compute()
{
volume = PI * radius * radius * height;
}
void Output()
{
printf(“The volume of the cylinder is %f\n“, volume);
}只有在确实必要的时候才需要了解这三个函数的具体实现细节与代码通过程序分割与逻辑分组,程序分离成一个一个的模块——在需要的时候,我们可以使用这些模块像积木一样构造整个程序功能分解与逐步求精Page *功能分解与逐步求精自顶向下的功能分解
先从整体考虑问题,将原始问题分解成逻辑上相互独立的多个部分;一一实现分解后的各部分,将上述实现组装成原始问题的解
功能分解必须按照程序需求进行,分解后的各部分应能实现为单入口单出口的函数
逐步求精
对于复杂系统,功能分解可能不会一步到位,对于某些部分可能需要重复上述功能分解步骤
在功能分解与逐步求精过程中,我们最关心的既不是数据对象,也不是具体算法,而是算法模式(程序范型)函数概述Page *函数概述函数是模块化的基本单位
主调函数与被调函数
程序、源文件与函数关系
程序中各模块关系函数的定义Page *函数的定义语法形式
类型标识符 函数名(形式参数表)
{ 说明部分
语句
}函数的调用Page *函数的调用调用前先声明函数原型:
在主调函数中按如下形式声明:
类型标识符 被调用函数名(含类型声明的形参表);
调用形式
函数名(实参)
嵌套调用
C语言不允许函数嵌套定义,但可以嵌套调用
递归调用
函数直接或间接调用自身例:求两数之和Page *例:求两数之和int main()
{ float add(float x,float y);
float a,b,c;
scanf("%f,%f",&a,&b);
c=add(a,b);
printf("sum is %f",c);
}
float add(float x,float y)
{ float z;
z=x+y;
return(z);
}运行结果:
输入:2.4, 5
输出:sum is 7.400000
例:用递归方法求n!Page *例:用递归方法求n!分析:
1 (n=0,1)
可用递归公式: n!=
n (n-1)! (n > 1)
要点:
定义一个函数fac()求n!,主函数中输入n 的值,调用fac()。用递归方法求n!Page *用递归方法求n!阶乘函数:
float fac(int n)
{
float f;
if (n<0) printf("n<0,data error!");
else if (n==0||n==1) f=1;
else f=fac(n-1)*n;
return(f);
}用递归方法求n!(续)Page *用递归方法求n!(续)main()
{
float fac(int n);
int n;
float y;
printf("input a integer number:");
scanf("%d",&n);
y=fac(n);
printf("%d!=%15.0f",n,y);
}
运行结果:
输入: 10
输出: 3628800例:用递归方法求Hanoi问题Page *例:用递归方法求Hanoi问题题目:
有三根针A、B、C。A针上有64个盘子,盘子大 小不等,大的在下,小的在上。要求把这64个 盘子从A针移到C针,在移动过程中可以借助B 针,每次只允许移动一个盘,且在移动过程中 在三根针上都保持大盘在下,小盘在上。
例:用递归方法求Hanoi问题(续)Page *例:用递归方法求Hanoi问题(续)分析
将n 个盘子从A针移到C针可以分解为下面三个步骤:
1.将A 上n-1个盘子移到 B针上(借助C针);
2.把A针上剩下的一个盘子移到C针上;
3.将n-1个盘子从B针移到C针上(借助A针)。
事实上,上面三个步骤包含两类操作:
1.将n-1个盘子从一个针移到另一个针上(n>1),这是一个递归的过程;
2.将1个盘子从一个针上移到另一针上。
于是用两个函数分别实现上面两类操作,用hanoi函数实现第一类操作,用move函数实现第2类操作。例:用递归方法求Hanoi问题(续)Page *例:用递归方法求Hanoi问题(续)void move(char getone,char putone)
{ printf("%c-->%c\n",getone,putone);}
void hanoi(int n,char one,char two,char three)
{
void move(char getone,char putone);
if (n==1) move (one,three);
else
{
hanoi (n-1,one,three,two);
move(one,three);
hanoi(n-1,two,one,three);
}
}例:用递归方法求Hanoi问题(续)Page *例:用递归方法求Hanoi问题(续)main()
{
void hanoi(int n,char one,char two,char three);
int m;
printf("input the number of diskes:");
scanf("%d",&m);
printf("the step to moving %3d diskes:\n",m);
hanoi(m,'A','B','C');
}例:用递归方法求Hanoi问题(续)Page *例:用递归方法求Hanoi问题(续)运行结果:
输入:3
输出:The step to moving 3 diskes:
A - -> C
A - -> B
C - -> B
A - -> C
B - -> A
B - -> C
A - -> C函数间的数据传递
——三种传递方式Page *函数间的数据传递
——三种传递方式返回值
参数
全局变量函数的返回值Page *函数的返回值通过 return 语句返回。
return 语句只能把一个返值传递给调用函数。
返回值的类型为定义的函数类型,若 return 语句中表达式类型与函数类型不一致,则转换为函数类型。
返回值可以是数值,也可以是地址。当返值是地址时,应该用指针接受它。
无返回值的函数,定义为 void 类型。函数间的参数传递Page *函数间的参数传递在函数被调用时才分配形参的存储单元。
实参可以是常量、变量或表达式。
实参类型必须与形参相符。
传递时是传递参数值,即单向传递。
使用地址作为参数,可以返回函数处理结果。
数组作为参数时的情况。
数组元素作实参,与单个变量一样。
数组名作参数,形、实参数都应是数组名,类型要 一样,传送的是数组首地址。例:实参与形参之间的值传递Page *例:实参与形参之间的值传递main()
{
int a=5, b=10;
void swap(int x,int y);
printf("%d,%d\n",a,b);
swap(a,b);
printf("%d,%d\n",a,b);
}
在内存中,实参单元与形参单元是不同的单元, 因此,在执行一个被调用函数时,形参的值如果发生变化时,并不会改变主调函数的实参的值。例:实参与形参之间的值传递(续)Page *例:实参与形参之间的值传递(续)void swap(int x,int y)
{
int temp;
temp=x;
x=y;
y=temp;
}
运行结果:
5 , 10
5 , 10使用全局变量传递数据Page *使用全局变量传递数据在所有函数之外说明的变量是全局变量,它在所有函数中都是可见的。
利用全局变量的这个特性,可以在函数间传递数据
注:变量的类型和作用范围变量的存储类型Page *变量的存储类型auto(自动)型
存放在栈区域中。属于一时性存储,其存储空间 可以被若干变量多次覆盖使用。
register(寄存器)型 (一般情况下不必这样指定)
存放在CPU的通用寄存器中
long,double,float不能设为register型,因为超过寄 存器长度
static(静态)型
存放在一般内存区域中变量的作用范围Page *变量的作用范围局部变量
在函数内部说明的变量
可以是: auto型、register型 —只能是局部变量 static型 —全局寿命、局部可见性
缺省为auto型
全局变量
在所有函数之前说明的变量
可以是: static型—只允许在本程序文件中引用 缺省型—可以被其它文件中的函数引用 但要在引用函数中用 extern 说明.例:局部变量的使用范围Page *例:局部变量的使用范围main()
{ int i;
static int a;
register int b;
void other(void);
i=1; a=10; b=5;
printf("---MAIN---\n");
printf("i:%d a:%d b:%d\n",i,a,b);
other();
printf("---MAIN---\n");
printf("i:%d a:%d b:%d\n",i,a,b);
}
i,a,b为局部变量,只在main()函数内有效。例:局部变量的使用范围(续)Page *例:局部变量的使用范围(续)void other(void)
{
int i;
static int a;
i=16; a=100;
printf("---OTHER---\n");
printf("i:%d a:%d\n",i,a);
}
i,a为局部变量,只在other()函数内有效。例:局部变量的使用范围(续)Page *例:局部变量的使用范围(续)运行结果:
---MAIN---
i:1 a:10 b:5
---OTHER---
i:16 a:100
---MAIN---
i:1 a:10 b:5例 全局变量的使用范围Page *例 全局变量的使用范围int a;
static int b;
main()
{
void next(void);
a=30;
b=100;
printf("a:%d b:%d\n",a,b);
next();
printf("--- a:%d b:%d\n",a,b);
}
void next(void)
{
a=a+b;
}a ,b为全局变量,其有效范围为从定义变量的位置开始到本源程序结束。例 全局变量的使用范围(续)Page *例 全局变量的使用范围(续)运行结果:
a:30 b:100
a:130 b:100不同类型变量的初始化Page *不同类型变量的初始化auto和register变量若被初始化,则每当进入该程序块后,都执行该变量的初始化赋值功能。
没有进行初始化的auto和register变量,其初值不定,不能直接在程序中使用。
static和全局变量若不初始化,自动赋0。
static和全局变量初始化时必须用常量为其赋初值,auto和register变量初始化时可以用常量或变量赋初值。
static型局部变量的初始化仅执行一次
extern型的变量不能进行初始化例 变量的初始化Page *例 变量的初始化int i=1;
main()
{ static int a;
register int b=-10;
int c=0;
void other(void);
printf("---MAIN---\n");
printf("i:%d a:%d b:%d c:%d\n",i,a,b,c);
c=c+8; other();
printf("---MAIN---\n");
printf("i:%d a:%d b:%d c:%d\n",i,a,b,c);
i=i+10; other(); }i 为全局变量。a为静态局部变量,全局寿命,局部可见。b, c为局部变量。例 变量的初始化(续)Page *例 变量的初始化(续)void other(void)
{
static int a=2;
static int b;
int c=10;
a=a+2; i=i+32; c=c+5;
printf("---OTHER---\n");
printf("i:%d a:%d b:%d c:%d\n",i,a,b,c);
b=a;
}a,b为静态局部变量,全局寿命,局部可见,有继承性。C为局部自动变量,每次进入都初始化。例 变量的初始化(续)Page *例 变量的初始化(续)---MAIN---
i:1 a:0 b:-10 c:0
---OTHER---
i:33 a:4 b:0 c:15
---MAIN---
i:33 a:0 b:-10 c:8
---OTHER---
i:75 a:6 b:4 c:15
运行结果内部函数和外部函数Page *内部函数和外部函数内部函数
只能被本文件中其它函数所调用,定义形式: static 类型标识符 函数名(形参表)
外部函数
可以为其它文件调用,定义形式: extern 类型标识符 函数名(形参表) extern缺省时隐含为外部函数
调用文件中也要用extern说明标准函数库举例Page *标准函数库举例标准 I/O 库
数学库
数字与字符特征库
辅助函数库标准 I/O 库接口Page *标准 I/O 库接口头文件:stdio.h
常用标准 I/O 库函数数学库接口Page *数学库接口头文件:math.h
常用数学库函数数学库应用示例Page *数学库应用示例输出0~360度之间,增量为30度的所有角度的正弦、余弦和正切函数值#include
#include
#define PI 3.14159
void main(){
int angle = 0; double radian, coef; coef = PI / 180;
for( ; angle <= 360; angle += 30 ){
radian = coef * angle;
printf( “sin(%d) = %lf, “, angle, sin(radian) );
…
}
}辅助函数库接口Page *辅助函数库接口头文件:stdlib.h
常用辅助函数库函数辅助函数库应用示例Page *辅助函数库应用示例修改学生电话簿管理程序,使用动态内存分配技术存储学生姓名struct student{
char name[30];
char gender;
char tel[20];
};原数据结构的缺点:姓名字段的长度固定,大多数姓名都不超过30字节,不过也可能存在超过30字节的情况,怎么办?struct student{
char* name;
char gender;
char tel[20];
};使用指针技术,在需要存储姓名时再分配恰当数目的内存辅助函数库应用示例Page *void Add( struct student stud[] )
{
int length = 0;
char buffer[255];
length = GetLength( stud );
do{
printf( “\nname : “ ); gets( buffer );
if( strcmp( buffer,““ ) == 0 ) break;
stud[length].name = ( char* )malloc( strlen(buffer) + 1 );
strcpy(stud[length].name, buffer );
…
}
while( ( toupper( temp[0] ) == 'Y' ) && ( ++length < MAX ) );
}辅助函数库应用示例辅助函数库应用示例Page *void Delete( struct student stud[] )
{
…
if( flag ){
printf( “%s : %c : %s will be deleted!\n“, stud[i].name,
stud[i].gender, stud[i].tel );
free( stud[i].name );
for( ; i < length – 1; i++ ) stud[i] = stud[i+1];
stud[i].name = NULL;
}
else printf( “Can‘t find %s!\n“, name );
}辅助函数库应用示例基本函数设计原则基本函数设计原则软件质量评判标准
正确性
程序性能
编程风格
可维护性
可重用性内聚性内聚性内聚性的性质
表达函数内部操作之间的相关性
高内聚性函数:通常仅操作单个数据对象
低内聚性函数:操作多个数据对象或执行多个操作,函数内部的部分代码相互无关
高内聚性函数的判断准则
可以使用动词名词词组命名函数内聚性示例函数内聚性示例高内聚性函数void ClearSymTbl( PSYMBOLTABLE p );
void DestroySymTbl( PSYMBOLTABLE p );低内聚性函数void ClearAndDestroySymTbl( PSYMBOLTABLE p );
不要编写低内聚性函数耦合度耦合度耦合度的性质
描述两个函数之间的数据流
参数传递的数目越少,耦合度越低
显式耦合度(函数参数)与隐式耦合度(全局变量)
函数耦合度的设计原则
参数个数尽可能少
尽量不使用全局变量进行函数通信函数耦合度示例函数耦合度示例隐式耦合度static int key;
static PNODE FindNode( PNODE u ){
…
while( u != NULL && u>key != key )
…
}显式耦合度static PNODE FindNode( PNODE u; int key ){
…
}
本文档为【C语言-函数-】,请使用软件OFFICE或WPS软件打开。作品中的文字与图均可以修改和编辑,
图片更改请在作品中右键图片并更换,文字修改请直接点击文字进行修改,也可以新增和删除文档中的内容。
[版权声明] 本站所有资料为用户分享产生,若发现您的权利被侵害,请联系客服邮件isharekefu@iask.cn,我们尽快处理。
本作品所展示的图片、画像、字体、音乐的版权可能需版权方额外授权,请谨慎使用。
网站提供的党政主题相关内容(国旗、国徽、党徽..)目的在于配合国家政策宣传,仅限个人学习分享使用,禁止用于任何广告和商用目的。