《C语言程序设计教程CJ04模块化程序设计New潭浩强第.ppt》由会员分享,可在线阅读,更多相关《C语言程序设计教程CJ04模块化程序设计New潭浩强第.ppt(49页珍藏版)》请在taowenge.com淘文阁网|工程机械CAD图纸|机械工程制图|CAD装配图下载|SolidWorks_CaTia_CAD_UG_PROE_设计图分享下载上搜索。
1、辽宁师范大学辽宁师范大学辽宁师范大学辽宁师范大学计算机与信息技术学院计算机与信息技术学院计算机与信息技术学院计算机与信息技术学院 蔡静蔡静蔡静蔡静2 2目目录录C C语言程序设计初步语言程序设计初步数据描述与基本操作数据描述与基本操作C C程序的流程控制程序的流程控制函数函数数组数组指针指针结构体和共用体结构体和共用体位运算位运算文件文件综合应用综合应用第四章第四章 函数函数4.1 函数概述函数概述4.2 变量的存储属性变量的存储属性4.3 编译预处理编译预处理4.1 函数函数 设计设计C语言的程序就是设计函数语言的程序就是设计函数n函数:把对应于一个问题求精的程序写在一起,作为一个单独的程序
2、模块,称为函数。n函数 源程序文件 C 程序n函数的分类n从用户角度分 标准函数/库函数 用户自定义函数 n按函数形式分 有参函数 无参函数例例4.1:4程序设计-2005秋Page Page 5 5 说明:类型说明符:表示函数返回值的类型 有返回值的:是某种类型(缺省为int 或 char)无返回值的:是 void 函数名:命名规则同标识符(且见名知义),函数名后紧跟一对小括号(无分号),且其后无分号。4.1.2 函数定义的一般形式函数定义的一般形式1.无参函数类型说明符类型说明符 函数名函数名()函数体函数体2.有参函数类型说明符类型说明符 函数名函数名(形参说明表列形参说明表列)函数体函
3、数体程序设计-2005秋Page Page 6 6 形参说明表列 写法:类型说明符类型说明符 形参形参,类型说明符,类型说明符 形参形参.形参与实参的类型、个数、位置必须一致。调用过程中存储空间的分配 实参可为常量、变量或表达式,各实参间用逗号分隔 值传递(实参 形参)函数体 一般包括说明定义部分和语句序列 也可为空,即占位以等待扩充功能。void 函数名函数名(void)程序设计-2005秋Page Page 7 7 函数的返回函数的返回-RETURN语句语句 格式:retnrn(.);.的类型应与函数类型一致,否则将之转化为指定类型。函数中可以有多个retnrn语句,执行到的第一个语句即为
4、被调用函数的结束。return 后的小括号可以省略。函数定义的外部性函数定义的外部性:函数不能嵌套定义函数不能嵌套定义(可以嵌套调用),一个函数不能定义在别的函数的内部。各函数相互独立。程序设计-2005秋Page Page 8 8int absolutevalue(int x)return(x=0?x:-x);void spc(int n)int i;for(i=0;in;i+);printf();return;程序设计-2005秋 函数定义与函数声明函数定义与函数声明在主调函数中要对在本函数中被调用的函数事先声明。在主调函数中要对在本函数中被调用的函数事先声明。n标准函数的声明标准函数的声
5、明n#include n#include “文件名”n自定义函数的声明自定义函数的声明:在调用函数之前说明被调用函数。n格式:类型说明符类型说明符 函数名函数名(形参说明表列形参说明表列);n说明:函数类型为int、char可以不作声明;被调用函数定义在调用函数之前,可不作说明;在所有函数定义之前声明,则在主调用函数中不再声明。n 一般,在文件开头,main()之外,声明函数。n注意:函数声明是一个语句,后跟分号9程序设计-2005秋 函数的调用函数的调用n调用的一般形式调用的一般形式 函数名函数名(实参表列实参表列)n调用的方式调用的方式n函数语句 (.);如:do_some();draw(
6、20);printf(“n*”);n 函数表达式如:c=max(a,b);c=max(a,b)*2+3;n 函数作为另一函数的参数。如:printf(“%d”,max(a,b);d=max(a,max(b,c);10程序设计-2005秋函数的调用是函数的调用是传值传值调用:调用:n一个入口:实参值传给形参。一个入口:实参值传给形参。n过程:形参在调用前不占内存,调用时才被分配存储单过程:形参在调用前不占内存,调用时才被分配存储单元,然后将实参值传给形参。元,然后将实参值传给形参。n一个出口:函数返回值。一个出口:函数返回值。main().f1();f2();.f1().f11();f2().f
7、21();f22();.f11().f21().f22().11程序设计-2005秋例1:#include stdio.hint add();void main()int x=3,y=7;printf(%d+%d=%dn,x,y,add(x,y);int add(int a,int b)return(a+b);12程序设计-2005秋例2:void swap(int x,int y)int temp;printf(n swap()中中交换前:交换前:n);printf(x=%d,y=%dn,x,y);temp=x,x=y,y=temp;printf(n swap()中中交换后:交换后:n);p
8、rintf(x=%d,y=%dn,x,y);/这样能交换变量的值?这样能交换变量的值?#include stdio.hvoid swap(int x,int y);int main(void)int a=3,b=5;printf(n main()中中交换前:交换前:n);printf(a=%d,b=%dn,a,b);swap(a,b);printf(n main()中中交换后:交换后:n);printf(a=%d,b=%dn,a,b);return 0;13程序设计-2005秋4.1.5 函数的嵌套调用函数的嵌套调用 例CJ_Combination.c:求 的值。1.接收接收m,n2.计算计算
9、m,n的组合的组合求某数的阶乘求某数的阶乘14程序设计-2005秋4.1.6 函数的递归调用函数的递归调用递归调用:调用一个函数的过程中又出现直接或间接调用该函数本身。n直接调用:A调用A.n间接调用:A调用B,B调用A.15程序设计-2005秋例:例:例CJ_Recursion_Age.c:有五个人坐在一起,问第五人的年龄,他说比第四人大2岁,第四人比第三人大2岁,第三人比二人大2岁,第二人比第一人大2岁,第一人是10岁。求第五人的年龄。age(n)=age(n-1)+2 age(1)=1016程序设计-2005秋例:例:main()printf(,age(5);age(5)int iold
10、;if(n1)iold=2+age(n-1);else iold=10;return iold;age(4)int iold;if(n1)iold=2+age(n-1);else iold=10;return iold;age(1)int iold;if(n1)iold=2+age(n-1);else iold=10;return iold;age(2)int iold;if(n1)iold=2+age(n-1);else iold=10;return iold;age(3)int iold;if(n1)iold=2+age(n-1);else iold=10;return iold;17程序
11、设计-2005秋例:例:例CJ_Recursion_factorial.c:用递归方法求n!n!=1 n=0,1 n!=n*(n-1)!n 118程序设计-2005秋例例CJ_Recursion_Hanoi.c:汉诺塔汉诺塔(Tower of Hanoi)问题问题只能用递归方法求解只能用递归方法求解游戏传说游戏传说:据传古代印度布拉玛庙里僧侣们玩一种称为汉诺塔的游戏,据说游戏结束就标志着世界末日的到来。游戏装置游戏装置:一块铜板,上面有三根杆,最左杆自下而上、由大到小顺序串有64个金盘,呈一个塔形。游戏要求游戏要求:把左边杆上的金盘全部移到最右边的杆上,条件是一次只能够动一个盘,并且不允许大盘
12、在小盘上面。推导推导:n个盘从一根杆移到另一根杆需要2n-1次,所以64个盘的移动次数为:264-1=18,446,744,073,709,511,615,这是一个天文数字,即使一台功能很强的现代计算机来解汉诺塔问题,每一微秒可能计算(不印出)一次移动,那么也需要几乎100万年。而如果每秒移动一次,则需近5800亿年。19程序设计-2005秋20程序设计-2005秋第一步:先把上面的第一步:先把上面的n-1个盘子设法借助个盘子设法借助b杆放到杆放到c杆,如杆,如箭头箭头所示,记做所示,记做hanoi(n-1,a,c,b)。第二步:把第第二步:把第n个盘子从个盘子从a杆直接移到杆直接移到b杆,如
13、箭头杆,如箭头所示所示第三步:把第三步:把c杆上的杆上的n-1个盘子借助个盘子借助a杆移到杆移到b杆,如箭头杆,如箭头所示,记做所示,记做hanoi(n-1,c,b,a)。a b ca b cno1no1no2no2no3no3no4no4no1no1no3no3no4no4no2no2假定僧侣们要把假定僧侣们要把n个盘子按题中的规定由个盘子按题中的规定由a杆借助杆借助c杆移到杆移到b杆。模拟这一过程的算法称为杆。模拟这一过程的算法称为hanoi(n,a,b,c)21程序设计-2005秋/汉诺塔问题汉诺塔问题#include stdio.hvoid hanoi(int n,char a,cha
14、r b,char c);void main(void)int n;printf(*n);printf(*tower of Hanoi *n);printf(*n);printf(Please enter the number of disks to be moved:);scanf(%d,&n);hanoi(n,a,b,c);void hanoi(int n,char a,char b,char c)if(n0)hanoi(n-1,a,c,b);printf(n Move disc%d from pile%c to%c ,n,a,b);hanoi(n-1,c,b,a);22程序设计-2005秋
15、h(3,a,b,c)h(2,a,c,b);no3:a-b;h(2,c,b,a);h(2,a,b,c)h(1,a,b,c);no2:a-c;h(1,b,c,a);h(1,a,b,c)h(0,a,c,b);no0:a-b;h(0,c,b,a);h(1,b,c,a)h(0,b,a,c);no1:b-c;h(0,a,c,b);h(1,c,a,b)h(0,c,b,a);no1:c-a;h(0,b,a,c);h(1,a,b,c)h(0,a,c,b);no1:a-b;h(0,c,b,a);h(2,c,b,a)h(1,c,a,b);no2:c-b;h(1,a,b,c);23程序设计-2005秋数组与函数数组与
16、函数函数不能返回多个值!函数不能返回多个值!u数组元素作函数参数数组元素作函数参数值传递。值传递。u将数组定义为外部将数组定义为外部/全局,以便相关函数都能对全局,以便相关函数都能对其操作。其操作。u数组名作函数参数数组名作函数参数地址传递。地址传递。u函数原型必须指明数组类型,还要用一对方括号说明它是数函数原型必须指明数组类型,还要用一对方括号说明它是数组,而数组的大小不是必须指定的;组,而数组的大小不是必须指定的;u实参可以只写数组名,这个数组名必须是已经定义为具有确实参可以只写数组名,这个数组名必须是已经定义为具有确定长度的数组名。定长度的数组名。例例CJ_Array1_06Functi
17、onArray.C:24程序设计-2005秋(a1)主调函数的代码读写变量,)主调函数的代码读写变量,被调函数实体不存在被调函数实体不存在(a21)主调函数的代码读写数组元素,)主调函数的代码读写数组元素,被调函数实体不存在被调函数实体不存在变量变量代码代码读写读写主调函数主调函数被调函数被调函数实体不存在实体不存在数组名数组名代码代码读写读写主调函数主调函数被调函数被调函数实体不存在实体不存在代码代码代码代码变量作参数与数组名作参数的分析比较:变量作参数与数组名作参数的分析比较:(a)函数调用前)函数调用前25程序设计-2005秋实参实参代码代码主调函数主调函数数组名数组名代码代码主调函数主
18、调函数被调函数被调函数被调函数被调函数代码代码代码代码数组名数组名传变量值传变量值传地址传地址形参形参形参形参实参实参(b1)系统创建函数实体,)系统创建函数实体,主调向函数主调向函数传传送变量送变量值值(b2)系统创建函数实体,)系统创建函数实体,主调向函数主调向函数传传送数组名(送数组名(地址地址)(b)函数调用时)函数调用时26程序设计-2005秋代码代码主调函数主调函数数组名数组名代码代码主调函数主调函数被调函数被调函数代码代码数组名数组名(c1)流程转到被调函数,)流程转到被调函数,被调函数读写函数中变量被调函数读写函数中变量(c2)被调函数按照数组名指示)被调函数按照数组名指示直接
19、读写主直接读写主调调函数建立的数组元素函数建立的数组元素被调函数被调函数代码代码变量变量读写读写读写读写(c)流程转到被调函数中)流程转到被调函数中27程序设计-2005秋(d1)流程转到主调函数,)流程转到主调函数,被调函数中实体被撤销,被调函数中实体被撤销,主调函数中的变量主调函数中的变量未被改变未被改变(d2)流程转到主调函数,)流程转到主调函数,被调函数中实体被撤销,被调函数中实体被撤销,主调函数中的数组元素可主调函数中的数组元素可被改变被改变变量变量代码代码读写读写主调函数主调函数数组名数组名代码代码读写读写主调函数主调函数被调函数被调函数变量变量被调函数被调函数数组名数组名代码代码
20、代码代码(d)函数返回)函数返回28程序设计-2005秋向函数传送二维数组向函数传送二维数组n规则:规则:n函数原型必须指明数组类型,还要用一对方括号函数原型必须指明数组类型,还要用一对方括号说明它是数组,而数组的大小不是必须指定的;说明它是数组,而数组的大小不是必须指定的;n实参可以只写数组名,这个数组名必须是已经定实参可以只写数组名,这个数组名必须是已经定义为具有确定长度的数组名。义为具有确定长度的数组名。例CJ_Array2_MatrixStudScore.C:有M个学生,每个学生学N门课,已知所有学生的各门课的成绩。为了分析教学情况,需要分别求每门课的平均成绩和每个学生的平均成绩。29
21、程序设计-2005秋int myStrLen(char s)int i=0,len=0;while(si+)len+;return len;自己编程实现字符串函数自己编程实现字符串函数CJ_Array1_Str03_FunctionsStdio.c:求字符串求字符串长长度函数度函数30程序设计-2005秋void myStrCpy()字符串复制函数字符串复制函数31程序设计-2005秋int myStrCat()字符串字符串连连接函数接函数32程序设计-2005秋int myStrCmp()字符串比字符串比较较函数函数33程序设计-2005秋4.2 变量的存储属性变量的存储属性存储属性包括3方
22、面,用register,auto,static,extern表示。n变量的存储器类型n主存主存n寄存器寄存器n变量的生存期/存在性n静态存储静态存储:在程序运行期间“永久性”占用固定内存的变量n动态存储动态存储:在程序运行期间根据需要进行临时性动态分配存储空间的变量n变量的作用域/可用域/可见性n全局全局n局部局部程序区程序区静态存储区静态存储区动态存储区动态存储区34程序设计-2005秋4.2.1 变量的作用域变量的作用域1.局部变量和全局变量局部变量和全局变量 n变量的作用域/可用域/可见性n全局全局:全局变量是定义在函数之外的变量。n局部局部:局部变量是定义在一个程序块内的变量。n程序块
23、可能是一个函数体(主函数),也可能是一个循环体或是选择结构中的一个分支语句块,也可以是任何一个用花括号扩起的语句块。n在C语言中,凡是声明在函数内部的变量都是局部变量。n一般说来,定义在什么范围的变量,其作用域就在那个范围,并且在从定义语句开始到这个域结束的范围内被使用,在这个域之外是不可见的。35程序设计-2005秋#include int main(void)int a=3;printf(a1=%dn,a);int b=5;printf(b1=%dn,b);printf(a2=%dn,a);return 0;b作用域a作用域3 35 53 336程序设计-2005秋Page Page 37
24、37C语言中每个变量和函数都有两个属性,即 存储类别存储类别 类型类型 变量表列变量表列数据类型数据存储类别int char 局部自动变量auto寄存器变量register静态局部变量static静态全局变量static外部变量extern C语言中变量的存储类型语言中变量的存储类型1.动态动态/静态存储方式静态存储方式程序区程序区静态存储区静态存储区动态存储区动态存储区程序设计-2005秋Page Page 3838 auto 类型说明符类型说明符 变量表列变量表列说明:只适用于局部变量(包括形参),随其所在函数调用开始而被分配内存,随其所在函数调用结束而释放内存(所以结束后值也随之丢失);
25、局部变量一般都是自动变量(auto是缺省值)。例:fun()int i;auto float f;.2、局部自动变量局部自动变量auto(动态动态)程序设计-2005秋Page Page 39393、局部静态变量、局部静态变量 static 类型说明符类型说明符 变量表列变量表列说明:函数调用结束后不释放内存,即保留数据值,下次调用该函数时可再次使用;系统自动赋初值0或0。例CJ_StaticAuto1.c:例CJ_StaticAuto2.c:程序设计-2005秋Page Page 4040#include void increment(void);int main(void)incremen
26、t();increment();increment();return 0;void increment(void)int x=0;/*auto*/x+;printf(%dn,x);运行结果:运行结果:111#include void increment(void);int main(void)increment();increment();increment();return 0;void increment(void)static int x=0;/*static*/x+;printf(%dn,x);运行结果:运行结果:123程序设计-2005秋Page Page 4141#include
27、int fac(int n);int main(void)int i;for(i=1;i=5;i+)/试一试试一试 for(i=0;i=4;i+)printf(%d!=%d n,i,fac(i);int fac(int n)static int f=1;/试一试试一试 int f=1;f=f*n;return f;程序设计-2005秋Page Page 42424、寄存器变量寄存器变量register register 类型说明符类型说明符 变量表列变量表列说明:只用于局部变量(包括形参),其他(局部静态变量、全局变量等)不行;一般变量存在内存中,但若程序需频繁访问某变量,用 register
28、 将其定义为寄存器变量就可以从寄存器中存/取数据;寄存器数目有限。现代的优化编译系统能识别使用频繁的变量并自动将其放在寄存器中,因此不必定义为寄存器变量。了解即可。程序设计-2005秋Page Page 43435、全局静态变量全局静态变量 static 类型说明符类型说明符 变量表列变量表列说明:全局变量无论是否 static,都属静态存储类别;static 说明该全局变量只能在本源文件中使用,别的源文件不得调用(主要用于多人编程时避免定义同名的全局变量)。程序设计-2005秋外部变量:定义在所有函数之外所有函数之外的变量称为外部变量。编译时静态存储静态存储。作用域从定义点到文件尾定义点到文
29、件尾。说明:n外部变量只用于全局变量全局变量;n外部变量使用的几种情况:nstatic静态外部变量:只能在本文件中使用nextern:对于定义点前面的函数,用extern使作用域扩充到需要用到他的函数。在一个 C 程序中全局变量只能定义一次,所以在一个模块中定义,其他模块中用 extern 声明为外部变量即可。n外部变量的定义只有一次,在所有函数之外;外部变量的声明可以多次,可在函数之内或之外。extern只用于声明,不用于定义。n例CJ_Extern_File:例:在一个文件的程序中使用外部变量n例CJ_Extern_Files:例:在多个文件的程序中使用外部变量6、外部变量外部变量 ext
30、ern 44程序设计-2005秋Page Page 4545 例例:void func1();void func2();static int x=500;main()auto int x=300;func1();func2();printf(“n Oringin x=%d”,x);void func1()x+=100;printf(“n Func1 x=%d”,x);return;void func2()auto int x=10;printf(“n Func2 x=%d”,x);return;X=300X=10X=500程序设计-2005秋变量的存储属性小结:变量的存储属性小结:存储属性存储
31、属性 register autostaticExtern存储位置存储位置 寄存器主存 主存主存生存期生存期动态动态 静态静态作用域作用域局部局部 局部或全局 全局46程序设计-2005秋Page Page 4747变量的作用域和生存期变量的作用域和生存期小结:小结:程序设计-2005秋4.2.3 内部函数和外部函数内部函数和外部函数指定函数能否被其他源文件调用:n内部函数内部函数/静态函数:静态函数:static 类型说明符类型说明符 函数名(形参表列)函数名(形参表列)n只能在本文件中被调用只能在本文件中被调用n外部函数:外部函数:extern 类型说明符类型说明符 函数名(形参表列)函数名(形参表列)n可以被其他文件调用可以被其他文件调用n例例CJ_ExternStatic_Files:多个源文件的使用:多个源文件的使用48程序设计-2005秋Thanks