资源描述
单击此处编辑母版标题样式,单击此处编辑母版文本样式,第二级,第三级,第四级,第五级,单击此处编辑母版标题样式,单击此处编辑母版文本样式,第二级,第三级,第四级,第五级,第,7,章 函数与预处理命令,C,语言程序设计,第1页,2,第,7,章 函数与预处理命令,7.1 概述,7.2 函数定义与调用,7.3 变量作用域与寄存方式,7.4 编译预处理,第2页,3,7.1,概述,程序构造清晰,可读性好。,减少反复编码工作量。,可多人共同编制一种大程序,缩短程序设计周期,提高程序设计和调试效率。,使用函数,好处,C,程序,源程序文件,n,函数,1,函数,m,源程序文件,1,函数,1,函数,n,第3页,4,【例7.1】求一种整数立方。,int cube(int x),/*,函数定义*,/,return(x*x*x);,main(),int f,a;,printf(nEnter an integer number,:,);,scanf(%d,f=,cube(a);,printf(%d*%d*%d=%dn,a,a,a,f);,程序运行状况如下:,Enter an integer number:2,2*2*2=8,函数调用,程序执行总是,从,main,函数开始,第4页,5,一种C源程序可以由一种或多种源程序文献构成。C编译系统在对C源程序进行编译时是以文献为单位进行。,一种C源程序文献可以由一种或多种函数构成。所有函数都是独立。主函数可以调用其他函数,其他函数可以互相调用。,在一种C程序中,有且仅有一种主函数main。C程序执行总是从main函数开始,调用其他函数后最终回到main函数,在main函数中结束整个程序运行。,阐明,第5页,6,函数种类,从函数定义形式分:,有参函数,在主调(用)函数和被调(用)函数之间通过参数进行数据传递,如:,int cube(int x),无参函数,如:getchar(),在调用无参函数时,主调函数不需要将数据传递给无参函数。,从使用角度看:,原则函数(库函数),库函数是由系统提供。如:getchar()、sin(x)等。在程序中可以直接调用它们。附录A列出了C部分库函数。,顾客自定义函数,如:例7.1中cube函数。,第6页,7,【,例,7.2】,无参函数定义与调用。,void wel e(),printf(*n);,printf(Wel e to China n);,printf(*n);,main(),wel e();,程序输出成果如下:,*,Wel e to China,*,第7页,8,7.2.1,函数定义,函数定义一般形式,函数类型 函数名(类型名 形式参数1,),阐明语句,执行语句,例如:求两个数最大值。,int max(int x,int y),int z;,z=x y?x:y;,return(z);,类型省略时默认为,int,类型,有形式参数,,max,为,有参函数,第8页,9,int max(x,y),int x,y;,int z;,z=x y?x:y;,return(z);,int max(x,y),int x,y;,或,int max(int x,y),或,int max(x,y),int x,y,z;,z=x y?x:y;,return(z);,花括号中也可认为空,这种函数叫空函数。,例如:null(),不能在函数体内定义其他函数,即函数不能嵌套定义。,形参也可以这样定义,以下定义都是错误,第9页,10,函数名(实参表列),在C语言中,把函数调用也作为一种表达式。因此但凡表达式可以出现地方都可以出现函数调用。例如:,wel e();,if(iabs(a)max)max=iabs(a);,m=max(c,max(a,b);,7.2.2,函数调用,函数调用一般形式:,第10页,11,int sum(),int i,t=0;,for(i=1;i=100;i+),t+=i;,return(t);,main(),int s;,s=sum();,printf(%dn,s);,程序输出成果:,5050,int sum(int x),int i,t=0;,for(i=1;iy?x:y;,return(z);,/*,返回,z,值*,/,main(),int a,b,c;,scanf(%d,%d,c=,max,(a,b);,printf(max is%dn,c);,第15页,16,函数返回值是通过return语句带回到主调函数。,功能:终止函数运行,返回主调函数,若有返回值,将返回值带回主调函数。,阐明:,若函数没有返回值,return语句可以省略。,return语句中表达式类型一般应和函数类型一致,假如不一致,VC系统警告同步自动将表达式类型转换为函数类型。,函数返回值,return,语句格式:,return (表达式);,return 表达式;,return;,第16页,17,【,例,7.8】,计算并输出圆面积。,s(int r),return 3.14*r*r;,main(),int r,area;,scanf(%d,printf(%dn,s(r);,自动转换为,int,型,思索:,若要得到双精度实型圆面积,程序应怎样修改,程序运行状况如下:,2,12,?,第17页,18,7.2.4,对被调函数申明和函数原型,变量要先定义后使用,函数也如此。即被调函数定义要出目前主调函数定义之前。如swap函数:,容许整型函数(且参数也是整型)定义出目前主调函数之后。如max函数:,假如非整型函数在主调函数之后定义,则应在主调函数中或主调函数之前对被调函数进行申明。,void swap(int x,int y),main(),swap(a,b);,main(),c=max(a,b);,max(int x,int y),第18页,19,对被调用函数阐明,假如使用库函数,应在文献开头用#include命令将调用库函数所需信息包括到文献中来。,如:#include“stdio.h”,stdio.h是开头文献,其中寄存了I/O库函数所用到某些宏定义信息。,#include“math.h”,使用数学库函数应用这条命令。,假如使用顾客自己定义函数,一般应在主调函数中对被调用函数作类型阐明。,main(),float add(float x,float y);,float a,b,c;,scanf(“%f,%f”,c=add(a,b);,printf(“sum is%f”,c);,float add(float x,float y),float z;,z=x+y;,return(z);,float add(float x,float y);,第19页,20,对被调函数进行申明一般形式,函数类型 函数名(,参数类型,1,参数名,1,,,),;,或 函数类型 函数名(,参数类型,1,,,参数类型,2,,,),;,思索:,如下哪种状况需要在主调函数中对被调函数申明,被调函数定义在前,主调函数定义在后。,主调函数定义在前,被调函数定义在后,且被调函数类型不是整型。,被调函数定义在后,但被调函数类型是整型。,第二种形式省略了参数名,此种形式也称为,函数原型,。,?,第20页,21,7.2.5,数组作函数参数,1.,一维数组元素作函数参数,main(),int a5,i,m;,for(i=0;i5;i+),scanf(%d,m=a0;,for(i=1;i5;i+),m=,min(m,ai,),;,printf(%dn,m);,【,例,】,求,5,个数中最小值。,int min(int x,int y),return(xy?x:y);,用打擂台措施求最小值。m相称于擂主,第21页,22,1.,一维数组名作函数参数,数组名表达数组在内存中起始地址。,例如:数组a在内存中从地址开始寄存,则a值为。是地址值,是指针类型数据(第8中将简介指针类型),不能把它当作是整型或其他类型数据。,实参是数组名,形参也应定义为数组形式,形参数组长度可以省略,但 不能省,否则就不是数组形式了。,【例】用冒泡法将10个整数排序。,第22页,23,void sort(,int,b,int n);,void printarr(,int,b,);,main(),int a10=11,22,63,97,58,80,45,32,73,36;,printf(Before sort:n);,printarr(a);,sort(a,10);,printf(After sort:n);,printarr(a);,void printarr(int,b10,),int i;,for(i=0;i10;i+),printf(%5d,bi);,printf(n);,void sort(,int,b,int n,),int i,j,t;,for(i=1;in;i+),for(j=0;jbj+1)t=bj;bj=bj+1;bj+1=t;,第23页,24,a0 a1 a2 a3 a4 a5 a6 a7 a8 a9,b0 b1 b2 b3 b4 b5 b6 b7 b8 b9,11 22 63 97 58 80 45 32 73 36,(a),排序前,a0 a1 a2 a3 a4 a5 a6 a7 a8 a9,b0 b1 b2 b3 b4 b5 b6 b7 b8 b9,11 22 32 36 45 58 63 73 80 97,(b),排序后,图,7.3,调用,sort,函数,b,形参 b 实际是一种,可以寄存地址变量,a:,实参赋给形参,首地址:,首地址:,第24页,25,#include stdio.h,main(),void scat(char str1,char str2);,char s150,s250;int i,k;,printf(Input s1:);,gets(,s1,);,printf(Input s2:);,gets(,s2,);,scat(,s1,s2,);,printf(Output s1:%sn,s1);,void scat(char,str1,char,str2),int i=0,k=0;,for(;str1i!=0;i+;);,for(;str2k!=0;i+,k+),str1i=str2k;,str1i=0;,scat,函数还可简化为:,void scat(char str1,char str2),int i=0,k=0;,for(;str1i;i+);,for(;str1i+=str2k+;);,【,例,】,编程序,实现字符串连接。,第25页,26,以二维数组为例。二维数组名作实参时,对应形参也应当定义为一种二维数组形式。对形参数组定义时可以指定每一维大小,也可以省略第一维大小阐明。,【例】编程序,将矩阵转置。设转置前为a矩阵,转置后为b矩阵,如下所示:,a=,1 2 3 4,5 6 7 8,9 10 11 12,1,5,9,2,6,10,3,7,11,4,8,12,b=,思绪:将,a00,b00,,,a01,b10,,,a02,b20,,,a10,b01,,,,,aij,bji,,,。,2.,多维数组作函数参数,第26页,27,void turn();,main(),int a34=1,2,3,4,5,6,7,8,9,10,11,12;,int i,j,b43;,turn(a,b);,printf(array b:n);,for(i=0;i4;i+),for(j=0;j3;j+),printf(%5d,bij);,printf(n);,/*,矩阵转置函数*,/,void turn(int,arra 4,int,arrb 3),int r,c;,for(r=0;r3;r+),for(c=0;c0;i-),a+=,sub2(i);,return a;,sub2(int n),return n+1;,程序输出成果:,9,第29页,30,2.,函数递归调用(略),(,1,)递归基本概念,递归调用:一种函数直接或间接地调用了它自身,就称为函数递归调用。,递归函数:在函数体内调用该函数自身。,int,sub,(int x),int y,z;,if()z=,sub,(y);,else ,return;,例如:,直接调用sub,函数自身,第30页,31,(,2,)递归函数执行过程,【,例,7.16】,编一递归函数求,n!,。,思绪,:以求,4,阶乘为例,:,4!=4*3!,,,3!=3*2!,,,2!=2*1!,,,1!=1,,,0!=1,。,递归结束条件,:当,n=1,或,n=0,时,,n!=1,。,递归公式:,n!=,1,(,n=0,1,),n(n-1)!,(,n1,),第31页,32,程序如下:,float fact(int n),float f=0;,if(n0),printf(n0,error!);,else if(n=0|n=1),f=1;,else f=,fact(n-1),*n;,return(f);,main(),int n;float y;,printf(nInput n:);,scanf(%d,y=fact(n);,printf(%d!=%-10.0fn,n,y);,运行状况如下:,Input a integer number:4,4!=24,第32页,33,递归调用过程,回 推,main()fact(4)fact(3)fact(2)fact(1),y=fact(4);f=4*fact(3);f=3*fact(2);f=2*fact(1);f=1;,return 24 return 6 return 2 return 1,递 推,第33页,34,7.3,局部变量和全局变量及其作用域,7.3.1,变量作用域,1.,局部变量及其作用域,变量作用域:变量在程序中可以被使用范围。,根据变量作用域可以将变量分为局部变量和全局变量。,局部变量(,内部变量,),:在,函数内,或,复合语句内,定义变量以及,形参,。,作用域,:函数内或复合语句内。,【例7.19】分析下面程序运行成果及变量作用域。,问题:一种变量在程序每个函数中都能使用吗?,第34页,35,void sub(int,a,int,b,),int,c,;,a=a+b;b=b+a;c=b-a;,printf(sub:ta=%d b=%d c=%dn,a,b,c);,局部变量,main(),int a=1,b=1,c=1;,printf(main:ta=%d b=%d c=%dn,a,b,c);,sub(a,b);,printf(main:ta=%d b=%d c=%dn,a,b,c);,int a=2,b=2;,printf(p:ta=%d b=%d c=%dn,a,b,c);,printf(main:ta=%d b=%d c=%dn,a,b,c);,局部变量,局部变量,程序输出成果:,main:a=1 b=1 c=1,sub:a=2 b=3 c=1,main:a=1 b=1 c=1,p:a=2 b=2 c=1,main:a=1 b=1 c=1,第35页,36,2.,全局变量及其作用域,全局变量(外部变量):在函数外部定义变量。,作用域:从定义变量位置开始到本源文献结束。如在其作用域内函数或分程序中定义了同名局部变量,则在局部变量作用域内,同名全局变量临时不起作用。,【,例,7.20】,全局变量和局部变量作用域。,第36页,37,int,a,=5;,void f(int,x,int,y,),int,b,c,;,b=a+x;,c=a-y;,printf(%dt%dt%dn,a,b,c);,局部变量,main(),int b=6,c=7;,f(b,c);,printf(%dt%dt%dn,a,b,c);,int,a=9,b=8,;,printf(%dt%dt%dn,a,b,c);,c,=10;,printf(%dt%dt%dn,a,b,c);,printf(%dt%dt%dn,a,b,c);,printf(%dt%dt%dn,a,b,c);,局部变量,局部变量,程序输出成果:,5 11 -2,5 6 7,9 8 7,9 8 10,9 8 10,5 6 10,全局变量,第37页,38,7.3.2 变量寄存类别及变量生存期,1.变量生存期与变量寄存分类,变量生存期:变量在内存中占据寄存空间时间。,思索:1.何时为变量分派内存单元?,2.将变量分派在内存什么区域?,3.变量占据内存时间(生存期)?,程序代码区,静态存放区,动态存放区,存放分配,动态寄存变量,静态寄存变量,第38页,39,2.变量寄存类别,变量属性,数据类型:,决定为变量分派内存单元长度;,数据寄存形式;,数范围。,寄存类别:,决定了变量生存期;,给它分派在哪个寄存区。,第39页,40,变量定义语句一般形式,寄存类别 数据类型 变量名1,变量名n;,auto(自动)register(寄存器)static(静态)extern(外部),A.自动变量(auto类别),局部变量可以定义为自动变量。,main()int,x,y,;,main(),auto,int,x,y,;,自动变量,等价,可省,第40页,41,内存分派,调用函数或执行分程序时在动态寄存区为其分派寄存单元,函数或分程序执行结束,所占内存空间即刻释放。,变量初值,定义变量时若没赋初值,变量初值不确定;假如赋初值则每次函数被调用时执行一次赋值操作。,生存期,在函数或分程序执行期间。,作用域,自动变量所在函数内或分程序内。,自动变量,第41页,42,B,静态变量(,static,类别),除形参外,局部变量和全局变量都可以定义为静态变量。,局部静态变量(或称内部静态变量),全局静态变量(或称外部静态变量),静态变量,静态变量,static,int,a,;,main(),float,x,y,;,f(),static,int,b,=1;,全局静态变量,局部静态变量,自动变量,不能省,第42页,43,内存分派,编译时,将其分派在内存静态寄存区中,程序运行结束释放该单元。,静态变量初值,若定义时未赋初值,在编译时,系统自动赋初值为0;若定义时赋初值,则仅在编译时赋初值一次,程序运行后不再给变量赋初值。,生存期,整个程序执行期间。,作用域,局部静态变量作用域是它所在函数或分程序。全局静态变量作用域是从定义处开始到本源文献结束。,静态变量,第43页,44,int,c,;,static int,a,;,main(),float,x,y,;,char,s,;,f(),static int,b,=1;,在函数外定义变量若没有用 static阐明,则是外部变量。外部变量只能隐式定义为extern类别,不能显式定义。,全局静态变量,自动变量,局部静态变量,外部变量,第44页,45,extern,数据类型 变量名,1,,,,变量名,n,;,或,extern,变量名,1,,,,变量名,n,;,注意:,外部变量申明用关键字extern,而外部变量定义不能用extern,只能隐式定义。,定义外部变量时,系统要给变量分派寄存空间,而对外部变量申明时,系统不分派寄存空间,只是让编译系统懂得该变量是一种已经定义过外部变量,与函数申明作用类似。,C.,外部变量(,extern,类别),第45页,46,【例7.25】在多文献程序中申明外部变量。,file1.c文献中程序如下:,int i;,main(),void f1(),f2(),f3();,i=1;,f1();,printf(tmain:i=%d,i);,f2();,printf(tmain:i=%d,i);,f3();,printf(tmain:i=%dn,i);,void f1(),i+;,printf(nf1:i=%d,i);,file2.c文献中程序如下:,extern int i;,void f2(),int i=3;,printf(nf2:i=%d,i);,void f3(),i=3;,printf(nf3:i=%d,i);,程序输出成果:,f1:i=2 main:i=2,f2:i=3 main:i=2,f3:i=3 main:i=3,申明外部变量,定义外部变量,第46页,47,D.寄存器变量(register类别),只有函数内定义变量或形参可以定义为寄存器变量。寄存器变量值保留在CPU寄存器中。,受寄存器长度限制,寄存器变量只能是char、int和指针类型变量。,【例7.26】寄存器变量使用。,main(),long int sum=0;,register int i;,for(i=1;i(Y)?(X):(Y),7.4,编译预处理(续),第55页,56,7.4,编译预处理(续),【,例,7.30】,带参数宏定义。,#define MAX(x,y),(x)(y)?(x):(y),main(),printf(%dn,a,b,MAX(a,b),);,printf(%dn,MAX(a+m,b+n),);,分两次替代:,将宏名MAX(a,b)替代成字符串(x)(y)?(x):(y)。,用实参a替代形参x,实参b替代形参y。,程序中两个printf语句被展开为:,printf(%dn,(a)(b)?(a):(b);,printf(%dn,(a+m)(b+n)?(a+m):(b+n);,第56页,57,7.4,编译预处理(续),【例7.31】分析下面程序运行后输出成果。,#define MA(x)x*(x-1),main(),int a=1,b=2;,printf(%dn,MA(1+a+b);,分两次替代:,MA(1+a+b)用x*(x-1)替代。,用1+a+b替代x。,printf语句被展开为:,printf(%dn,1+a+b*(1+a+b-1);,尤其注意:,由于替代文本中x没有用括号括起,因此,1+a+b也不能用括号括起。,程序输出成果:8,第57页,58,7.4,编译预处理(续),命令一般形式,格式1:#include,格式2:#include 文献名,7.4.3 文献包括,文献包括是将指定某个源文献内容所有包括到目前文献中。用include命令实现。,用格式1,预处理程序仅在VC98INCLUDE目录下查找指定文献。,用格式2,预处理程序首先在目前目录中查找指定文献,若找不到再到VC98INCLUDE目录中查找。,第58页,59,7.4,编译预处理(续),例如:调用sin(x)函数时,要在程序开头使用如下命令:,#include,在预处理时,用math.h文献内容替代,#include 命令行。,2功能,在预处理时,将include命令后指定文献内容替代该命令行。,第59页,60,7.5 多文献程序调试措施,措施一操作环节如下:,在VC+环境下建立file1.c源文献,编辑完毕后进行编译,产生工作组;,在工作组活动状态下(即不关闭),再依次创立新源文献file2.c和file3.c;,在file1.c文献窗口激活状态下,编译连接运行。,1.使用工作组(Workspaces)将多种文献连接成一种可执行文献措施,例如:将file1.c、file2.c和file3.c合并成一种可执行文献。,第60页,61,7.5 多文献程序调试措施(续),措施二操作环节如下:,可事先在文本编辑器中,将file1.c、file2.c和file3.c编辑好;,然后在VC环境中,编译file1.c,产生工作组;,点击Project Add To Project Files,依次将file1.c和file2.c加入到工作组中;,在file1.c文献窗口激活状态下,编译连接运行。,第61页,62,7.5 多文献程序调试措施(续),2.使用文献包括命令将多种文献连接成一种可执行文献措施,例,分析文献包括后源程序状况。,#include file2.c,#include file3.c,file1.c,file3.c,file2.c,A,B,C,第62页,63,7.5 多文献程序调试措施(续),file3.c,#include file2.c,#include file3.c,file1.c,A,file2.c,B,C,B,C,A,file1.c,预处理前,预处理后,第63页,64,本章结束,第64页,
展开阅读全文