资源描述
C,程序设计基础教程,第六章,函数与宏定义,*,*,/49,第六章 函数与宏定义,6.1 函数概念,6.2 变量作用域和存储类型,6.3 内部函数与外部函数,6.4 递归函数设计和调用,6.6 综合范例,10/2/2024,1,6.1 函数概念,C,语言允许把问题设计成一个一个的模块,程序通过调用模块功能来解决问题。这些模块通常都是通过函数来实现的,又可称其为,函数模块,。,C,语言中,函数可分为两类 :,一类是由系统定义的,标准函数,,又称为,库函数,,其函数声明一般是放在系统的,include,目录下以.,h,为后缀的头文件中,如在程序中要用到某个库函数,必须在调用该函数之前用,#,include,命令将库函数信息包含到本程序中。,另一类函数是,自定义函数,,两种形式:,第一种:,函数声明、函数调用、函数定义。,第二种:,函数定义、函数调用。,10/2/2024,2, 6.1.1 函数定义,函数定义的一般形式可以有两种。,形式一:,存储类型符 返回值类型符 函数名(形参说明表),函数语句体,形式二:,存储类型符 返回值类型型符 函数名(形参表),形参说明;,函数语句体,10/2/2024,3,说明:,1,存储类型符,指的是函数的作用范围,它只有两种形式:,static,和,extern。,static,说明函数只能作用于其所在的源文件,用,static,说明的函数又称为,内部函数,。,extern,说明函数可被其它源文件中的函数调用,用,extern,说明的函数,又称为,外部函数,。,缺省,情况为,extern。,2,返回值类型符,指的是函数体语句执行完成后,,函数返回的值的类型,,如,int,, float, char,等等,若函数无返回值,则用,空类型,void,来定义函数的返回值。,缺省,情况为,int,型。,10/2/2024,4,3,函数名,由任何合法的标识符构成。建议将函数名的命名与函数内容有一定关系。,4在第一种函数定义的形式中,,形参说明表,是一系列用逗号分开的每个形参变量说明。,如:,int,x,int,y,int,z,这表示形参变量有三个:,x, y, z。,它们的类型都是,int,型。,在第二种函数定义的形式中,,形参表,是一系列用逗号分开的形参变量。如:,x, y, z,5,函数语句体,是放在一对花括号 中,由局部数据类型描述和功能实现两部分组成。,10/2/2024,5,6,函数返回语句,的形式有以下两种:,函数无返回值的情况:,return;,函数有返回值的情况:,return(,表达式的值);,在第种情况下要注意,“表达式的值”的类型必须与函数返回值的类型相一致。,例如:求两个任意整数的绝对值的和,用函数,abs_sum(),实现。,/*,直接调用库函数来计算,m,和,n,的绝对值 */,int,abs_sum(,int,m,int,n),return (abs(m)+abs(n);,/*,函数,abs(),是在头文件,math.h,中声明的*/,10/2/2024,6,函数定义如下:,int,abs_sum(,int,m,int,n),if (m0),m=-m;,if(n0),n=-n;,return(m+n);,6.1.2 函数声明和调用,一函数的声明,函数声明的一般形式:,存储类型符 返回值类型符 函数名(形参说明表);,如:,int,abs-sun(,int,m,int,n);,10/2/2024,7,二函数调用,函数调用是通过函数调用语句来实现的,分两种形式:,无返回值的情况:,函数名(实参表);,有返回值的情况:,变量名函数名(实参表);,该变量名的类型必须与函数的返回值类型相同。,函数调用时都会去执行函数语句中的内容,函数执行完毕后,回到函数的调用处,继续执行下面的语句。,10/2/2024,8,6.1.3 函数的传值方式,函数的传值方式,:,采用实参表将每一个实参的值相应地传递给每一个形参变量,形参变量在接收到实参表传过来的值时,会在内存,临时开辟新的空间,,以保留形参变量的值,当函数执行完毕时,这些,临时开辟的内存空间会被释放,,并且形参的值在函数中不论是否发生变化,,都不会影响到实参变量的值的变化,,这就是函数的传值方式。,自定义函数在程序中的使用顺序有两种形式:, 先进行函数声明,再进行函数调用,,函数定义放在函数调用之后。,函数声明在函数调用之前。,函数定义放在函数调用之前。,10/2/2024,9,【例6-1】 编程序,通过调用函数,abs-sum(),,求任意两个整数的绝对值的和。,/*,exam6_1.c,调用函数求两整数绝对值的和*/,#,include ,int abs_sum(int m,int n);,main(),int x,y,z;,scanf(%d%d,z=abs_sum(x,y);,printf(sum is %d,z);,int abs_sum(int m,int n),if(m0),m=-m;,if(n0),n=-n;,return m+n;,程序运行结果:,7 12,sum is 19,10/2/2024,10,用传值方式调用函数时,实参也可以是函数调用语句,【例6-2】求任意三个数的绝对值的和。,/*,exam6_2.c,调用函数求三个整数绝对值的和*/,#,include ,int abs_sum(int m,int n);,main(),int x,y,z,sum;,scanf(%d%d%d,sum=abs_sum(,abs_sum(x,y),z);,printf(sum is %d,sum);,int abs_sum(int m,int n),if(m0),m=-m;,if(n0),n=-n;,return m+n;,程序运行结果:,7 12 5,sum is 24,10/2/2024,11,注意:,对于有返回值的函数,调用时若没有把它赋给某个变量,仍然是可以的,只是函数的返回值有可能会被丢失。,【例6-3】 求任意两数的乘积。,自定义一个函数,mul,(),,用于求两数的乘积,程序:,/*,exam6_3.c,求两个数的乘积*/,#,include ,float mul(float a,float b);,main(),float x,y,z;,scanf(%f %f,z=mul(x,y);,/* */,x=x+10;,y=y-10;,mul(x,y); /*, */,10/2/2024,12,x=x*2;,y=y*2;,printf(z=%f,mul(%f,%f)=%fn,z,x,y,mul(x,y); /*,*/,float mul(float a,float b),return a*b;,程序运行结果:,5 6,z=30.000000,mul(30.000000,-8.000000)=-240.000000,10/2/2024,13,程序说明,:,注释处调用函数后的返回值赋给变量,z。,注释处调用函数后的返回值没有赋给任何变量,函数的返回值被丢失。,注释处调用函数后的返回值成为了,printf,(),函数的参数。,10/2/2024,14,6.2 变量作用域和存储类型,一变量的作用域,变量的作用域:指的是,变量的有效范围,,针对变量不同的作用域,可把变量分为局部变量和全局变量。,局部变量,:在函数内部或某个控制块的内部定义的变量为局部变量,局部变量的有效范围只限于,本函数内部,,退出函数,该变量自动失效。,全局变量,:在函数外面定义的变量称为全局变量,全局变量的作用域是从该变量定义的位置开始,直到源文件结束。在,同一文件,中的所有函数都可以引用全局变量。,10/2/2024,15,局部变量和全局变量的,作用域,如图所示:,10/2/2024,16,【例6-4】 变量作用域应用举例,阅读下面的程序,注意区分局部变量和全局变量的作用域。,/*,exam6_4.c,变量作用域举例*/,#,include ,void a( void );,void b( void );,void c( void );,int x = 1;,main(),int x = 5;,printf(local x in outer scope of main is %dn, x );,10/2/2024,17,int x = 7;,printf( local x in inner scope of main is %dn, x );,printf( local x in outer scope of main is %dn, x );,a();,b();,c();,a();,b();,c();,前三次,输出,结果:,local x in outer scope of main is 5,local x in inner scope of main is 7,local x in outer scope of main is 5,10/2/2024,18,printf( local x in main is %dn, x );,getchar();,return 0;,void a( void ),int x = 25;,printf( nlocal x in a is %d after entering an, x );,+x;,printf( local x in a is %d before exiting an, x );,10/2/2024,19,void b( void ),static int x = 50;,printf( nlocal static x is %d on entering bn, x );,+x;,printf( local static x is %d on exiting bn, x );,void c( void ),printf( nglobal x is %d on entering cn, x );,x *= 10;,printf( global x is %d on exiting cn, x );,10/2/2024,20,程序运行结果:,后6次,函数调用,local x in a is 25 after entering a,local x in a is 26 before exiting a,local static x is 50 on entering b,local static x is 51 on exiting b,global x is 1 on entering c,global x is 10 on exiting c,local x in a is 25 after entering a,local x in a is 26 before exiting a,local static x is 51 on entering b,local static x is 52 on exiting b,global x is 10 on entering c,global x is 100 on exiting c,最后一次输出:,local x in main is 5,10/2/2024,21,二变量的存储类型,变量的存储类型:指的是变量的,存储属性,,它说明,变量占用存储空间的区域。,在内存中,供用户使用的存储区由,程序区,、,静态存储区,和,动态存储区,三部分组成。,变量的存储类型有四种:,auto,型、,register,型、,static,型和,extern,型。,auto,型,变量存储在内存的,动态存储区,。,register,型变量保存在,寄存器,中。,static,型变量和,extern,型变量,存储在,静态存储器,。,10/2/2024,22,局部变量,的存储类型缺省值为,auto,型 。,全局变量,的存储类型缺省值为,extern,型 。,auto,型变量和,register,型变量只用于定义局部变量。,static,型变量即可定义成局部变量,又可定义成全局变量。,【例6-5】 设计一个函数:,long,fac,(,int,n),,可用来计算15的阶乘。,分析:可在函数中定义一个,static,型变量,用来保存上次的计算结果。,10/2/2024,23,/*,exam6_5.c,用,static,型变量保留上次阶乘的值*/,#,include ,long fac(int n),static int f=1;,f=f*n;,return f;,main(),int i;,for(i=1;i=5;i+),printf(%d!=%ldn,i,fac(i);,程序运行结果:,1!=1 2!=2 3!=6 4!=24 5!=120,局部变量,f,被定义成,static,型的,因此,它只在该函数第1次被调用的时候初始化其值为1,以后再调用该函数时,不再进行初始化,而是使用上一次调用的值。,10/2/2024,24,6.3 内部函数与外部函数,一内部函数,若函数的存储类型为,static,型,则称其为,内部函数,或称,静态函数,,它表示在同一个程序中(由多个源文件组成),该函数只能在一个文件中存在,在其它文件中不可使用。,如:,static,int,fun-name();,内部函数只能被其所在的源文件调用。,二外部函数,若函数的存储类型定义为,extern,型,则称其为,外部函数,,它表示该函数能被其它源文件调用。函数的缺省存储类型为,extern,型。,注意:,在需要用到外部函数的文件中,其函数声明必须用,extern,进行说明。,10/2/2024,25,例如:有两个源文件,file1.c,和,file2.c,如下所示:,/*,file1.c,调用外部函数*/,#,include ,int,mod(,int,a,int,b);,extern,int,add (,int,m,int,n); /*,外部函数声明*,main(),int,x, y, result;,scanf,(“%d%d”, ,result=add(x,y); /*,调用外部函数*/,if (result 0),result=result-mod(x,y);,printf,(“result=%dn”, result);,10/2/2024,26,int,mod(,int,a,int,b),return(a%d);,/* file2.c,外部函数*/,extern,int,add(,int,m,int,n),return(m+n);,说明:,1在文件1(,file1.c),中的函数声明:,int,mod(,int,a,int,b);,实际上相当于:,extern,int,mod(,int,a,int,b);,10/2/2024,27,2在文件2(,file2.c),中的函数定义:,extern,int,add(,int,m,int,n),return(m+n);,实际上相当于:,int,add(,int,m,int,n),return(m+n);,3由多个源文件组成一个程序时,,main(),函数只能出现在一个源文件中。,10/2/2024,28,4多个源文件的连接方式有三种:,将各源文件分别编译成目标文件,得到多个目标文件(.,obj,后缀),然后用连接命令(,tlink,),把多个.,obj,文件连接起来,在,Turbo c,上用如下命令:,tlink,file1.,obj,+file2.,obj,+,filen,.,obj,生成一个,file1.exe,的可执行文件。,建立项目文件(.,prj,后缀),具体操作可参阅各种,C,编译手册。,使用文件包含命令。,10/2/2024,29,6.4 递归函数设计和调用,C,语言中一个函数中的语句可以是对另一个函数的调用。,函数嵌套调用,图例:,调用过程按图中箭头所示的方向和顺序进行,属于一种,线性调用关系,,每次调用后,最终返回到原调用点,继续执行以下语句。,10/2/2024,30,C,语言中还允许在函数中,调用自身,,或函数之间相互调用,这种调用方式称之为,递归,。递归又分为,直接递归调用,和,间接递归调用,。,直接递归调用;函数直接调用自身。,间接递归调用:函数互相调用对方。,直接递归:,int,temp (,int,x),int,y, z;,z=temp(y);,10/2/2024,31,间接递归:,显然,递归有可能陷入无限递归状态,最终导致错误发生。因此,,设计一个递归问题必须具备两个条件,:,1后一部分与原始问题类似。,2后一问题是原始问题的简化。,10/2/2024,32,【例6-6】 编程,从键盘输入一个正整数,n,,求,n!。,n!,的数字表达式为:,n!=,定义一个求,n!,的函数:,long,fac,(,int,n),long,fac,(,int,n), long result;,if (n= = 0 | n= =1),result =1;,else,result=n*,fac,(n-1);,return(result);,10/2/2024,33,完整程序如下:,/*,exam6_6.c,用递归法求,n!*/,#include ,long fac(int n),long result;,if(n=0|n=1),result=1;,else,result=n*fac(n-1);,return result;,main(),int x;,long f;,10/2/2024,34,scanf(%d,if(x1),设计一个函数:,long,fibonacci,(,int,n),用于计算数列中第,n,项的值,,10/2/2024,36,程序如下所示:,/*,exam6_7.c,求第,n,项,Fibonacci,数列的值*/,#,include ,long fibonacci(int n);,main(),int x=0;,long result;,do,result=fibonacci(x);,printf(fibonacci(%d)=%ldn,x,result);,scanf(%d,while(x!=-1);,10/2/2024,37,long fibonacci(int n),if(n=0|n=1),return n;,else,return fibonacci(n-1)+fibonacci(n-2);,程序运行结果:,fibonacci(0)=0,3,fibonacci(3)=2,4,fibonacci(4)=3,6,10/2/2024,38,以,x=4,为例,下图说明了,fibonacci,函数是怎样计算,fibonacci,(4),的。图中把,fibonacci,简写成,f。,10/2/2024,39,6.6 综合范例,【例6-12】 在屏幕上画一个1818大小的棋盘。,程序如下:,/*,exam6_12.c,在屏幕上画一个棋盘*/,#,include ,#include ,#include ,/*,定义画棋盘所需的制表符*/,#,define LU 0xda /*,左上角*/,#,define RU 0xbf /*,右上角*/,#,define LD 0xc0 /*,左下角*/,#,define RD 0xd9 /*,右下角*/,#,define L 0xc3 /*,左边*/,#,define R 0xb4 /*,右边*/,10/2/2024,40,#,define U 0xc2 /*,上边*/,#,define D 0xc1 /*,下边*/,#,define CROSS 0xc5 /*,十字叉*/,/*棋盘左上角坐标*/,#,define MAP_X 5,#define MAP_Y 5,void draw_cross(int x,int y);,void draw_map();,main(),textmode(C40);,draw_map();,10/2/2024,41,/*函数定义:*/,void draw_map() /*,画棋盘*/,int i,j;,for(i=0;i19;i+),for(j=0;j19;j+),draw_cross(i,j);,void draw_cross(int x,int y), gotoxy(x+MAP_X,y+MAP_Y);,textcolor(GREEN);,if(x=0 & y=0),putch(LU); /*,画左上角*/,return;,10/2/2024,42,if(x=0 & y=18),putch(LD); /*,画左下角*/,return;,if(x=18 & y=0),putch(RU); /*,画右上角*/,return;,if(x=18 & y=18),putch(RD); /*,画右下角*/,return;,10/2/2024,43,if(x=0),putch(L); /*,画左边*/,return;,if(x=18),putch(R); /*,画右边*/,return;,if(y=0),putch(U); /*,画上边*/,return;,10/2/2024,44,if(y=18),putch(D); /*,画下边*/,return;,putch(CROSS); /*,画十字叉*/,程序中,宏定义的制表符的值,可从,ASC,码表中查得,如:0,xbf,为十六进制表示,它代表右上角制表符“”。,该程序必须在纯,DOS,模式下运行,其运行结果是在屏幕上显示一个绿色的1818大小的方格棋盘,,如后图所示。,10/2/2024,45,该程序显示一个绿色的1818大小的方格棋盘,10/2/2024,46,【例6-13】 将一个字符串的字符反向输出到屏幕。,采用递归函数调用方法,程序如下:,/*,exam6_13.c,反向显示字符串*/,#,include ,#include ,void backwards(char s,int index); /*,函数声明*/,main(),char str80;/*,定义字符数组*/,int index = 0;,strcpy(str,Show this string.); /*,字符串拷贝*/,backwards(str,index); /*,函数调用*/,10/2/2024,47,void backwards(char s,int index) /*,函数定义*/,if (sindex),printf(%c,sindex); /*,输出字符*/,backwards(s,index+1); /*,递归调用*/,printf(%c,sindex); /*,输出字符*/,程序运行结果:,Show this string.gnirts siht wohS,10/2/2024,48,小结,:,介绍了函数的定义和传值调用函数的使用方法 。,注意:若用全局变量作为函数的参数,则在函数中可以使得该全局变量的值发生变化。,对于递归函数的设计一定要有可使递归结束的条件,否则会使程序产生无限递归。,使用预处理命令时,要注意以下几点:,1宏替换定义的末尾不能使用分号“;”。,2在有参数的宏定义中,参数加括号和不加括号有时会有区别。,3使用文件包含时,要避免出现变量和函数发生重定义的现象。,4要区分条件编译与条件语句的作用。,10/2/2024,49,
展开阅读全文