第4章 函数与C程序结构

上传人:沈*** 文档编号:244072722 上传时间:2024-10-02 格式:PPT 页数:88 大小:650.50KB
返回 下载 相关 举报
第4章 函数与C程序结构_第1页
第1页 / 共88页
第4章 函数与C程序结构_第2页
第2页 / 共88页
第4章 函数与C程序结构_第3页
第3页 / 共88页
点击查看更多>>
资源描述
,单击此处编辑母版标题样式,单击此处编辑母版文本样式,第二级,第三级,第四级,第五级,程序设计,技术,C,语言数据描述和,C,程序设计初步,结构化程序设计基础和,C,语言的控制结构,数组及其应用,函数与,C,程序结构,指针与函数,指针与数组,字符串及其应用,结构体类型和联合体类型,C,语言的文件处理及其应用,位运算与枚举类型,函数与程序结构,函数的定义和调用,函数的嵌套调用和递归调用,变量的作用域和生存期,编译预处理,多源文件,C,程序的组织方法,函数与程序结构,模块化程序设计技术就是通过开发和维护一些小的程序块(即模块)的方法构建一个大型程序,是人类解决较大的复杂问题所采用的一种“分而治之”的策略。本章主要讨论,C,语言实现模块化程序设计技术的手段以及在模块化实现过程中所遇到的一系列问题,。,4.1,函数的定义和调用,C,程序的一般结构,C,源程序,源文件,1,源文件,i,源文件,n,预处理语句,函数,1,函数,n,说明,/,定义部分,执行部分,图,4.1 C,程序结构,4.1.1,函数的定义和声明,函数定义就是编写完成某种具有一定功能的程序模块。,1),现代风格的函数定义形式:,返回类型符 函数名,(,形式参数表及其说明,),/,函数头,变量定义和说明及函数执行语句,/,函数体,函数体:,由变量定义与函数执行语句组成。二者全无则是,空函数,先占位置,以后补上。,/*,现代风格*,/,long,fac(int,n),/,函数头, long y=1;,int i;,for(i=1;i=n ;i+),y=y*i;,return(y,);,4.1.1,函数的定义和声明,函数体,long,为函数返回值类型,return,语句将,n!,返回调用函数,调用函数处获取,n,的值,函数的定义内容如下:,函数返回值类型,可是任何有效类型,,void,表示函数无返回值。,函数名,用户自定义标识符,不能重名。,形式参数(简称形参)的数目、类型,为函数接受外来数据提供变量名、类型和数目。,return ,语句,函数执行结果,(,按函数定义的返回类型,),返回给主调函数。如果函数定义时返回类型为,void,,可缺省,return,语句。,4.1.1,函数的定义和声明,注意:,C,语言中的每一个函数都是一个独立的代码块。一个函数的代码块是隐藏于函数内部的,不能被任何其它函数中的任何语句(除调用它的语句之外)所访问。它既不能影响程序其它部分,也不受其它部分的影响。,在一个函数的内部不能定义其他函数(即函数不能嵌套定义)。这个规定保证了每个函数都是一个相对独立的程序模块。,由多个函数组成的,C,程序中,各个函数的定义顺序是任意的,它不影响,C,程序运行时函数的执行顺序,4.1.1,函数的定义和声明,2),函数的声明,在主调函数中,要对本函数将要调用的函数事先进行必要的声明。所谓“声明”是指向编译系统提供必要的信息:函数名,函数的返回值类型,函数参数的个数、类型及排列次序,以便编译系统对函数的调用进行检查。例如,检查形参与实参类型是否一致,函数返回值的类型是否正确。,C,语言的函数分为标准库函数和用户自定义函数两大类。下面分别介绍他们的声明方法。,4.1.1,函数的定义和声明,标准库函数的声明,使用标准库函数时,由于系统提供的标准库函数的说明都分门别类集中在一些称为,“头文件”,的文本文件中,所以在程序中如果要调用系统标准库函数,也要在程序的适当位置使用编译预处理语句来进行声明。,例如:,#include ,或,#,inlcude,“stdio.h”,作用:,将调用有关库函数的必要信息包含到本源文件中来。,4.1.1,函数的定义和声明,用户自定义函数的声明,对于用户自定义函数,如果被调用函数(简称被调函数)与调用它的函数(简称主调函数)在同一源文件中,在函数调用之前,需要对被调函数进行声明。,被调函数声明的一般形式:,返回类型符 函数名,(,形式参数表及其说明,);,4.1.1,函数的定义和声明,#include ,void main(),long,factorial(int,n);,int num;,printf(Input,the num: );,scanf(%d,&num,);,printf(%d,!= %ld,n,num,factorial(num,);,long,factorial(int,n),/,函数,factorial,的定义,inti;,long fact=1;,for(i=1;i=n;i+),fact*=i;,return fact;,函数声明告诉编译系统,factorial,是返回类型,long,,只有一个,int,参数的函数,程序演示,4.1.1,函数的定义和声明,在函数声明中,形参变量名字无关紧要,(可与函数定义不同或缺省),factorial,的声明语句可写成如下两种形式:,long,factorial(int,); /*,缺省形参名 *,/,long,factorial(int,x); /*,形参名与定义不同 *,/,注意:,在下列情况下可以不对被调函数进行声明:,被调函数的定义出现在主调函数之前,不必进行说明,其原因是编译系统此时已经知道了被调函数的返回类型。,程序演示,4.1.1,函数的定义和声明,函数调用,一个函数调用另外一个函数以完成某一特定的功能称之为函数调用。,调用者称为主调函数,被调者称为被调函数,函数调用的一般形式:,函数名(实参表);,调用时填入的参数,称为实际参数,简称实参。,实参的个数、类型和顺序,应该与被调函数的形参个数、类型和顺序一致。,4.1.2,值参数传递的函数调用,三种调用函数的方式,函数语句,(p119),:,将函数调用作为一个单独的,C,语句,此种方式主要对应于返回值为空类型(,void,)的函数调用。,函数表达式:,函数作为一项,出现在表达式中,以函数返回值参与表达式的运算。这种方式要求函数是有返回值的。如:,c=,add(a,b,);,函数实参:,函数调用作为另外一个函数调用的实际参数出现。此时要求函数被调用后必须要返回一个确定值以作为其外层函数调用的实际参数,。,如:,if (,prime(,reverse(n),),),4.1.2,值参数传递的函数调用,C,程序由若干个相对独立的函数组成。在程序运行过程中,当被调函数是有参函数时,函数调用必然伴随着参数传递。当一个函数调用另一个函数时,实参的值传递到形参变量中就实现了主调函数到被调函数间的,数据,传递。,函数间参数传递的两种方式,传值方式,传地址方式,4.1.2,值参数传递的函数调用,函数调用时实参的种类,实参是值参数,变量,常数,表达式,实参是地址值参数,指针变量,变量的地址,(&a),数组名或某数组元素地址,函数名,4.1.2,值参数传递的函数调用,例,4.3,传值方式,#include ,void main(),int a=10,b=5;,void,swap(int,x, int y);,swap(a,b);,/*,函数调用语句*,/,printf(“swap,调用后:,a=%d,b=%dn”,a,b);,void,swap(int x, int y),int temp;,temp=x,x=y,y=temp;,printf(“swap,调用中:,x=%d,y=%d”,x,y);,函数定义:一个函数实体,函数声明语句,4.1.2,值参数传递的函数调用,为被调函数的形参变量,(,局部变量,),分配存储;,实际参数拷贝到对应形式参数(拷贝完成后形参与实参无关);,控制转到被调函数执行;,程序控制流程从被调函数返回主调函数(系统自动,撤消为被调函数建立的形参局部变量);,10,5,a,b,y,x,temp,10,5,10,5,10,函数调用执行过程与参数传递,swap,函数的功能是交换两个参数的值。但运行,结果表示,它只交换了两个形参变量,x,和,y,的值,而,没有交换,main,(),中两个实参,a,与,b,的值。,调用时,形参,y,形参,x,10,5,10,5,实参,a,实参,b,形参,y,形参,x,实参,a,实参,b,10,5,5,10,交换后,图,4.2,程序运行结果,:,swap,调用前:,a=10,b=5,swap,调用中:,x=5,y=10,swap,调用后:,a=10,b=5,4.1.2,值参数传递的函数调用,例,4.3,传值调用,重要结论:,函数的,传值调用方式,是一种数据复制,方式。在这种方式下,实参值通过复制,的方式传递给形参变量,实参与形参各,自占用内存不同的存储单元,当数据传,递结束后,它们互不相干。,因此,在被调函数中无论,形参,怎样,变,化,,都,不会影响,主调函数中,实参的值,。,函数受外界影响减小到最小限度,从,而保证了函数的独立性。,4.1.2,值参数传递的函数调用,4.1.3,指针与地址值参数传递的函数调用,如果要在被调函数中对主调函数的实参单元进行操作,则需要将主调函数中实参的内存地址值传递给被调函数对应的形参单元。,在,传地址方式,下,被调函数中用于接收对应地址值的,形参,必须是,指针变量,或,数组,。,本小节主要介绍指针变量的基本用法和,地址值参数传递的,函数调用。,1,)指针与指针变量的概念,指针,(,一个变量的地址,),指针就是地址,。在程序设计中,系统根据变量定义的数据类型为其分配一定长度的内存单元。一旦分配完成,变量与内存单元的,首地址,就建立了一种对应关系,(,见图,1),。,指针变量,(,用来存放另一变量的地址,),内存单元地址是用,有序,整型数,(,无符号整型数,),进行编址,。为了能够操作这些,地址量,,有必要构造一种变量来存储它们,这种变量称为“指针变量”(见图,2,)。,4.1.3,指针与地址值参数传递的函数调用,变量和地址的对应关系,(,图,1),房间,变量名 房客,变量值 房间号,地址,short,i,j; char,ch,; float f;,i,=5;,j,=3;,ch,=H;,f,=3.14;,5,3,H,3.14,i,j,ch,f,2000,2002,2004,2005,2009,2001,2003,2006,2007,2008,编译或函数调用时为变量分配内存单元,内存中每个字节有一个编号,地址,变量,是对程序中数据,存储空间的抽象,指针与指针变量的概念,(,图,2),.,.,2000,2004,2006,2005,整型变量,i,20,变量,ptr,2001,2002,2003,2000,指针,指针变量,变量的,内容,变量的,地址,指针变量,ptr,变量,i,i,的地址,2000,20,指向,i,地址存入,指针变量,指针变量的定义,数据类型符,*,指针变量名,1,*,指针变量名,2,;,int,i,j,*p1, *p2; float f; float *pf;,合法标识符,表示定义指针变量,不是*运算符,注意:,int *p1,*p2;,指针变量名是,p1,p2 ,不是,*,p1,*p2,。,指针变量只能指向定义时所规定类型的变量,。,指针变量定义后,变量值不确定,,使用,前必须先赋值,。,指针变量所指向目标变量的数据类型,2,)指针变量的赋值与初始化,int x, *y=,float *j=,void,*p=&x,指针变量只能指向与它同,类型的普通变量,但任何类型变量的地址可赋值给,void,型,(,空类型,),的指针变量。,例,int *p=,无确定地址,int,i;,p=1000,;,只能存放地址,例,int,x,*y;,y=,&,x;,指针变量赋值的一般形式,初始化方式为指针变量赋值,数据类型符,*,指针变量名,=,初始地址值;, 指针变量赋值的一般形式:指针变量名,=,地址值,;,其中地址值表示为:,&,例,int *p;,p=NULL;,p=0;,为指针变量,p,赋空值,&,&,为取地址运算符,作用是返回操作对象的地址。,*,*,为指针运算符(或称为,间接运算符,),作用是返回以操作对象的值作为地址的内存单元里的内容。,x,y,100,25000,25000,int x,*,y=,y,&x,*y,x,scanf(“%d”,scanf(“%d”,y,);,printf(“%dn”,x,);,printf(“%d,”,*y,);,x=100;,*,y=100;,*,(,*,与,&,同级,从右到左结合,),3,)指针变量的引用,例,4.4,程序演示,指针变量作为函数的参数,使用指针变量作为函数的参数实现的是,传地址值调用,,其主要特征为:,主调函数中,以数据对象存放的地址值,(,地址或指针变量,),作为,实参,调用另外一个函数;,被调函数的,形参,必须是可以接收地址值的,指针变量,;,实参和形参数据类型必须相同,传地址值调用与传值调用的区别,P1(,形参,),a,地址,a,地址,a,地址,*p1,a,P,重要结论,传地址值调用,时,数,据在主调函数和被调函数中均,使用同一存储单元,,所以在被调函数中对数据任何的变动必然会反映到主调函数中。,4),地址值参数传递调用,a,地址,/*,指针变量作参数,*,/,void,swap(int,*,x,int,*y), int t;,t=*x;,t=a;,*x=*y;,a=b;,*y=t;,b=t;,/*,普通变量作参数,*,/,void,swap(int,x,int,y), int t;,t=x;,x=y;,y=t;,10,5,10,5,5,10,a,b,y,x,a,b,x,y,a,地址,b,地址,a,地址,b,地址,*,x,*,y,a,地址,b,地址,t,t,4),地址值参数传递调用,例,4.3,值传递,例,4.5,地址值传递,当函数调用传递的是,地址值,时,能否在被调函数中改变主调函数中实参的值取决与在被调函数中,对参数的操作方式:,在被调函数中,若操作形参指针变量所指向的对象,,则可以在被调函数中改变主调函数中的实参值。,在被调函数中,若操作的是形参指针变量本身,,则仍然实现的是传值调用,并不能改变主调函数中的实参值。,5,)指针变量与被指针指向变量的区别,/*,被调函数中,操作指针指向的对象,*,/,void,swap(int,*,x,int,*y),int t;,t=*x;,*x=*y;,*y=t;,操作指针变量指向的对象,因而可以改变主调函数中实际参数的值。,/*,被调函数中,操作指针变量本身,*,/,void,swap(int,*,x,int,*y,),int *t;,t=x;,x=y;,y=t;,操作指针变量本身,因而不能改变主调函数中实际参数的值。,5,)指针变量与被指针指向变量的区别,例,4.6,程序演示,10,5,a,b,y,x,t,a,地址,b,地址,a,地址,a,地址,b,地址,操作指针变量指向的对象,交换或改变了实参的值。,操作指针变量本身,,交换了实参的地址,不改变实参值,5,)指针变量与被指针指向变量的区别,4.1.4,数组参数传递的函数调用,在,C,程序中,既可以用数组元素作为函数的参数,也可将数组整体作为函数的参数。,使用,数组元素,作为参数传递,是实现函数间的,传值调用,。,将数组整体作为参数传递时,用,数组名或某数组元素的地址,作为函数的实参,实现的是函数间的,传地址值调用,。,4.1.4,数组参数传递的函数调用,/*,ex04-07.cpp,*/,#include ,#include ,#include ,#define N 5,void main(),void,myprint(int,x);,int,aN,bNN,i,j,;,srand(time(NULL,);,printf(,数组,a.n);,for(i=0;iN;i+),ai,=rand()%100;,myprint(ai,);,printf(,数组,b.n);,for(i=0;iN;i+),for(j,=0;j,N;j,+),bij,=rand()%100;,myprint(bij,);,printf(n,);,void,myprint(int,x,),printf(%4d,x);,例,4.7,数组元素作值,传递,23,40,一维数组作函数的参数,数组名或某元素地址作为函数的实参,实参数组将它的全部或部分存储区域提供给形参数组共享,,即形参数组与实参数组是同一存储区域或是实参数组存储区域的一部分。当函数调用结束后,形参数组消失,主调函数的数组就保存了形参数组操作的结果。,使用,数组名,或,数组第一个元素的地址,是把整个实参数组传递给被调函数。,使用,某个数组元素的地址,作为主调函数的实参可以实现传递部分数组元素到被调函数。,a,b,s / *s,v /*v,a,或,&a0,作实参,b+i,或,&,bi,作实参,4.1.4,数组参数传递的函数调用,4.1.4,数组参数传递函数调用,例,4.8,编制求和,函数并通过该函,数求数组的各元,素和。,数组名,a,作实参,数组,v,为形参,数组,a,和,v,共享全,部存储单元。,#include ,#define N 10,void main(), int sum(int v,int n);,int total;,int aN=1,2,3,4,5,6,7,8,9,10;,total=sum(,a,N);,printf(total=%ldn,total);,int sum(int,v,int n), int i,s=0;,for(i=0;in;i+),s+=,vi,;,return s;,例,4.8,数组,名作地址传递,4.1.4,数组参数传递函数调用,例,4.9,编制求和函数并通过该函数求数组自某一元素后的所有元素和,起始点元素序号从键盘输入。,/* Name:ex04-09.cpp */,#include ,#define N 10,void main(),int sum(int v,int n);,int total,pos;,int aN=1,2,3,4,5,6,7,8,9,10;,printf(,请输入求和起始元素序号,: );,scanf(%d,total=sum(,&,apos,N-pos);,printf(total=%ldn,total);,int sum(int,v,int n), int i,s=0;,for(i=0;in;i+),s+=,vi,;,return s;,部分元素的地址,传递,&,apos,是将数组自,apos,元素以后提供给形参,v,共享,N-pos,是共享数组元素个数,7,3,7,4.1.4,数组参数传递函数调用,数组的起始地址,数组的起始地址,a,平面起始地址(,二级地址,),&,a00,线性起始地址,(,一级地址,),a0,线性起始地址,(,一级地址,),*,a,线性的起始地址,(,一级地址,),图,4.8,二维数组起始地址的表示,二维数组作函数的参数,二维数组存储时也是有序地占用一片连续的内存区域,,数组名,表示这段存储区域的,首地址,。,注意:,二维数组的起始地址有多种表示法,有平面起始地址和线性起始地址之分。,二维数组,起始地址,的等价表示,:,a,a0,&a00,*a,a,a0,a1,a2,a3,&a00,4.1.4,数组参数传递函数调用,使用二维数组名作为实参,用二维数组名作为函数参数实现的是,“传地址调用”,,其本质是实参数组将它的全部存储区域提供给形参数组共享,即形参数组与实参数组是同一存储区域。,实参,a,形参,v5,图,4.9,实参为二维数组名,例,4.10,程序演示,4.1.4,数组参数传递函数调用,使用二维数组起始地址的一级地址形式作为实参,在实际程序设计中,有时需要函数能够处理任意行列长度的二维数组,则用二维数组作为形参的设计形式就不适合。,为了编制较为通用的函数,可使用二维数组作为实参,一维数组作为形参。如图,4.10,所示:,实参,a0,形参,v,形参数组,v,本质上是指针变量,图,4.10,形参数组与实参数组全部共享存储区域,4.1.4,数组参数传递函数,调用,实现参数传递时的注意点:,函数调用的实参必须是,一级地址形式,,同时将二维数组的行数和列数传递到被调函数中。,在被调函数中将传递来的二维数组当作一维数组处理。其二维数组元素在线性连续存储单元中的排列序号,即对应的一维数组元素如下:,aijvi*n+j,例,4.11,程序演示,序号,函数与程序结构,函数的定义和调用,函数的嵌套调用和递归调用,变量的作用域和生存期,编译预处理,多源文件,C,程序的组织方法,4.2,函数的嵌套调用和递归调用,4.2.1,函数的嵌套调用,C,语言规定:,在,C,程序中所有的函数都是平行的。即在,C,程序中,函数不能嵌套定义,但函数可以嵌套调用,,即一个函数在被调用的过程中又调用了另外的一个函数。一个两层嵌套函数调用的过程如下图所示:,被嵌套调用函数,4.2.1,函数的嵌套调用,4.2.1,函数的嵌套调用,例,4.12,:,计算,s=1,k,+2,k,+3,k,+,N,k,程序设计思路:,可以把问题分解为两个模,块:求幂次方模块和求和模块,在求和模块中,要包含求幂次方模块。,f2(),函数的参数为,n,和,k,,,其返回值是,n,的,k,次,方。,f1(),函数的参数为,n,和,k,返回值是幂次方的,累加和。,4.2.1,函数的嵌套调用,#include ,long f2(int,n,int,k),long power=n;,int i;,for(i,=1;i,k;i,+),power *= n;,return power;,long f1(int,n,int,k),long sum=0;,int,i;,for(i,=1;i1),递归结束条件:,n!=1 (n=1),递归算法描述:,n1,递归公式,n=0,或,n=1,f(n,),return 1,return,f(n-1)+f(n-2),T,F,图,4.13,菲波拉契数列算法,例,4.15,程序演示,4.2.2,函数的递归调用,通过三个示例的分析,递归方式的实现也是基于,C,语言的条件控制结构。递归函数设计的基本框架是相对固定的。,其一般形式描述如下:,if,递归结束条件成立,Return,已知结果,else,将问题转化为同性质的较简单子问题;,以递归方式求解子问题(递归公式);,函数与程序结构,函数的定义和调用,函数的嵌套调用和递归调用,变量的作用域和生存期,编译预处理,多源文件,C,程序的组织方法,一个变量的性质可以从两方面进行分析:,变量的作用域:,变量能够起作用的空间范围。,变量的生存期:,其变量值存在的时间范围。,它们的基本意义如下:,一个变量在某个函数、某个源程序文件或某几个源程序文件范围内是有效的,则称其有效的范围为该,变量的作用域,,在此范围内可以访问或引用该变量。,一个变量的值在某一时刻是存在的,则认为这一时刻属于该,变量的“生存期”,,或称其在此时刻“存在”。,4.3,变量的作用域和生存期,为了能够有效地确定变量的作用域和生存期两项属性,,C,语言用,存储类别,对变量进行限定。,变量定义的完整形式为:,存储类别符, ,变量表,;,其中:,数据类型说明变量的取值范围及允许的操作;,存储类别用于指定变量在内存中的存放方法。,变量的存储类别有四种,自动型(,auto,),寄存器型(,register,),静态型(,static,),外部参照型(,extern,),4.3,变量的作用域和生存期,可在程序的三个位置定义变量,:,函数内部、函数的参数定义和函数外部。,按变量的作用域分类,全局变量(外部变量):,指定义在所有函数外的变量,其作用范围从定义处开始到源文件结束为止。全局变量的定义形式:,extern ,变量表,;,局部变量(自动变量):,有三个位置可定义函数形参;函数体内部;复合语句内部。其作用域限定在定义范围内。局部变量的定义形式:,auto ,变量表,;,4.3.1,变量的作用域,全局变量与局部变量的关系,int x;,void main(),void f1(), int,x,;, int,x,; ,v,oid f2(),外部变量,(int x),在本源程序文件中没有定义!,x,x,x,4.3.1,变量的作用域,例,4.18,全局变量,的作用域,4.3.1,变量的作用域,局部变量,(,自动变量,),的作用域,局部变量的建立和撤消由系统自动进行。如在某函数中定义了自动变量,只有该函数被调用时,系统才为函数范围内的局部变量分配存储单元;当函数执行完毕,自动变量被系统自动撤销。,在变量定义中省略,auto,的均为自动变量。,自动变量的值在一个函数的两次调用之间不会保留。,不同函数中定义的,同名,局部变量之间是毫无关系的。,4.19,程序演示,4.20,程序演示,4.3.1,变量的作用域,同名全局变量与局部变量作用域重叠问题,即在某些特定的情况下,出现全局变量、函数内定义的局部变量、复合语句中定义的局部变量,名字相同,的现象。即在全局变量与局部变量作用域重叠的情况下,,C,语言规定按,“定义就近原则”,来引用变量。,如果定义的全局变量与函数中局部变量,同名,,程序进入函数内,使用定义的同名局部变量。,在复合语句中如果定义有与较大范围(函数局部或全局)变量同名的变量,则使用该小局部范围内定义的同名局部变量;,同名全局变量与局部变量作用域重叠问题,4.21,程序演示,0,x=,20,x=,10,x=,0,用户使用内存存储空间的情况,程序区,静态存储区,动态存储区,用户区,所有的全局(外部)变量及静态局部变量都存放在静态存储区,程序执行完毕才释放。,函数的形参,局部(自动)变量,4.3.2,变量的生存期,程序运行中,不同存储类别的变量,占用的存储区域不同,分配的存储时间,(,生存期,),也不同。,按变量的生存期分类,局部变量,(,自动变量,),的生存期:,这类变量存储于内存的,动态存储区,,它在程序运行中,使用到该变量的时间段存在,。即程序进入该函数或复合语句时才分配存储空间,当该函数或复合语句执行完后存储空间被撤销。,全局变量或静态变量,(,全局或局部,),的生存期:,这类变量存储于内存的,静态存储区,,它在编译时分配存储空间,在程序运行的,整个期间都存在,。,4.3.2,变量的生存期,在,C,程序设计中,为了合理选择变量的存储类别,有必要对不同存储类别的变量在程序中的作用分两方面进行讨论。,全局变量的存储类别,对于全局变量而言,能够起作用的存储类别为,extern,和,static,。,局部变量的存储类别,对局部变量能够起作用的存储类别为,auto,和,static,。,1,),全局变量的存储类别,extern,扩展作用域,static,限制作用域,限制强于扩展,,即若两种存储说明对某全局变量同时出现,以限制说明为准。,int x;,static,int,y;,extern int x;,extern int x;,extern,int,y;,y,x,x,y,是静态全局变量不能扩展作用域,X,原作用范围,File1.cpp,源文件扩展,x,File2.cpp,源文件扩展,x,使用,extern,声明,可扩充全局变量在一个源程序的作用域,例,4.22,程序演示,1,),全局变量的存储类别,X,原作用域,X,被扩充后的作用域,100,110,130,自动变量的生存期与其所在函数被调用运行的时间相同,并且自动变量的值在函数的多次调用中都不会保留。,为满足在函数的多次调用中,局部变量仍能在保持原来值基础上继续使用,,C,语言提供了静态存储类别,(static),。,静态局部变量的定义形式:,static,数据类型符 变量表;,2,)局部变量的存储类别,静态局部变量具有如下特点:,静态局部变量的存储空间在程序的整个运行期间是固定的。系统在,编译时,就为它,分配,存储空间,它的,生存期是整个程序的运行期间。,静态局部变量的初始化是在程序编译时进行的。如果在定义时没有对它进行初始化,那么系统将它自动初始化为,0,(整型)、或,0,(字符型)。,静态局部变量的值在函数多次调用中具有可继承性。,静态局部变量的值只能在定义它的局部范围内使用。在它的作用域范围之外,,该静态局部变量虽然存在,但不能对它进行访问,。,2,)局部变量的存储类别,例,4.23,静态局部变量与自动变量的比较,1,/* Name: ex04-23.cpp */,2,#include ,3,void main(),4, void f1();,5,f1();,6,f1();,7,8,void f1(),9,int,a=10;,10,static int b=10;,11,a+=100;,12,b+=100;,13,printf(a,=%,d,b,=%,dn,a,b,);,14,例,4.23,程序演示,2,)局部变量的存储类别,110,110,a=110,b=110,110,210,a=110,b=210,b+;,函数与程序结构,函数的定义和调用,函数的嵌套调用和递归调用,变量的作用域和生存期,编译预处理,多源文件,C,程序的组织方法,编译预处理概念,编译预处理是,C,编译系统在对,C,源程序进行编译之前对它进行的一些预加工,然后再将处理的结果和源程序一起进行编译,以得到目标代码。恰当地使用,C,语言的编译预处理功能可以有效地提高程序开发效率。,编译预处理语句以,#,号开头、占用一个单独的书写行、语句的结尾不需要用“;”作为结束符。,编译预处理语句可以出现在,C,源程序的任何位置,其作用范围是从出现点开始到源程序末尾。,编译预处理语句常有三种形式,宏定义,文件包含,条件编译,4.4,编译预处理,宏定义分为代参数的宏定义和不代参数的宏定义,不代参数的宏定义,定义形式:,#define,宏标识符 字符串,调用形式,:,宏标识符,(,直接用在表达式中,),宏定义的作用:,在宏定义的作用范围之内,将所有的宏标识符用指定的字符串替换。,字符串:,可以是字符串常量、已定义的宏、表达式或语句组成的字符串。,注意,:,使用,#define,宏标识符,语句可撤消宏定义。,4.4.1,宏定义,4.4.1,宏定义,例,4.24,宏定义预处理,/* Name: ex04-24.cpp */,#include ,#define PI 3.1415926,#define R 2.0,void main(),double circum();,double area();,printf(“circum,=%,fn,circum,();,printf(area,=%,fn,area,();,double circum(),return 2.0*PI*R;,double area(),return PI*R*R;,例,4.24,程序演示,不进行宏代换的情况,宏名出现在一个标识符中,例:,#define,loc,12345,int,loc,al;,int,12345,al; (,不会进行这种替换,),宏名出现在字符串常量中,例:,#define,PI,3.14,语句,printf(“The,value of,PI,is: %,fn”,PI,);,结果是:,The value of,PI,is:,3.140000,4.4.1,宏定义,此处不替换,4.4.1,宏定义,例,4.25,宏调用替换问题的理解,/* Name: ex04-25.cpp */,#include ,#define N 2,#define M N+2,#define MN 2*M,void main(),int x=MN;,printf(x,=%,dn,x,);,错误理解,:,N2,M4,(,2+2,),MN8,(,2*4,),输出结果,:x=8,正确理解:,MN2*N+2,MN2*2+2,正确结果:,x=6,程序演示,带参数的宏定义,定义形式:,#define,宏标识符,(,形参表,),表达式,调用形式:,宏标识符,(,实参表,),宏调用的作用:,将所有的宏标识符用指定的表达,式替换并且用实际参数代替表达式中的形式参数。,注意:,为了避免当实际参数是表达式时引起的宏,调用错误,最好将宏定义中表达式样式字符串的形,式参数用圆括号括起来。,4.4.1,宏定义,4.4.1,宏定义,例,4.26,代参数宏定义使用示例,/* Name: ex04-26.cpp */,#include ,#define PI 3.14159,#define,S(r,) PI*r*r,#define,S(r,) PI*(r)*(r),void main(),double a,b,area1,area2;,a=3.3;,b=3.2;,area1=,S(a,);,area2=3.14159*(a+b)*(a+b),area2=,S(a+b,);,错误展开,:,area2=3.14159*,a+b,*,a+b,printf(area1=%fnarea2=%fn,area1,area2);,程序演示,修改后展开,带参数的宏与函数的主要区别:,函数的调用需要进行控制的转移,而带参数的宏仅仅是表达式的替换。,带参数的宏没有一个确定的类型。在宏中随着带入的实参类型的不同,其结果的类型随之而变。,函数调用时,对实参有数据类型的限制,要求与定义类型一致;带参数的宏进行调用时没有实参数据类型的限制,实参可以是任意类型。,函数调用存在从实参向形参传递数据的过程,而带参数的宏调用中不存在传递过程,因而宏调用一般比函数调用具有较高的时间效率。,4.4.1,宏定义,4.4.1,宏定义,例,4.27,宏调用替换问题的理解,/* Name: ex04-27.cpp */,#include ,#define Min(x,y) (x)(y)?(x):(y),void main(),int a=1,b=2,c=3,d=4,t;,t=,Min(a+b,c+d,)*1000;,t=(a+b)(c+d)?(a+b):,(c+d)*1000,printf(t=%dn,t);,正确结果为:,t=,程序演示,3,4.4.2,文件包含,文件包含的一般形式:,#include ,或,#include “,文件名”,文件包含的,功能:,在编译本源程序文件之前,将指定的文件整个内容嵌入到本文件之中。,文件包含中,使用尖括号,:意味着指示编译系统按系统设定的标准目录搜索被包含的文件;,文件包含中,使用双引号,:意味着按指定的路径搜索,未指定路径时则在当前目录中搜索。,条件编译,使用条件编译可以对,C,语言的源程序内容进行有选择性地编译。条件编译可有效地提高程序的可移植性,并广泛地应用在商业软件中,为一个程序提供各种不同的版本。,条件编译命令常用形式一,#if,#,elif,#else,#,endif,例,4-28,输入字符,根据需要设置条件编译,使之能将字母全改为大写字母输出,或全改为小写字母输出,直到键入,#,结束。,4.4.3,条件编译,4.4.3,条件编译,使用条件编译可为一个程序提供各种不同的版本。,宏名,SYSTEM,替换为,BSD,测试,BSDSYSV,宏名,SYSTEM,替换为,BSD,测试,BSD=,BSD,宏名,HDR,替换为,”,bsd.h,”,#include “,bsd.h,”,函数与程序结构,函数的定义和调用,函数的嵌套调用和递归调用,变量的作用域和生存期,编译预处理,多源文件,C,程序的组织方法,多源文件,C,程序概念,C,程序是由函数组成的模块化结构程序。组成一个程序的所有函数模块可以放在同一个源程序文件中,也可以分别放在几个不同的源程序文件中。一般情况下,一个较大的,C,程序可以由若干个源文件组成。,多源文件,C,程序的组织方法,使用文件包含的方法;,使用工程文件的方法;,4.5,多源文件,C,程序的组织方法,4.5.1,使用文件包含的方法,使用文件包含的方法,使用预编译语句,#include,将其它,C,源程序文件包含到本源文件,将它们组合成为一个完整的,C,程序。,/*Name: ex04-30a.cpp */,#include ,#include ex04-30b.cpp,void main(),int n;,printf(Input,the n: );,scanf(%d,&n,);,printf(%d,!=%,ld,n,fac(n,);,/*Name: ex04-30b.cpp*/,long,fac(long,n),if(n,=1),return(1);,else,return(fac(n-1)*n);,程序演示,
展开阅读全文
相关资源
正为您匹配相似的精品文档
相关搜索

最新文档


当前位置:首页 > 管理文书 > 施工组织


copyright@ 2023-2025  zhuangpeitu.com 装配图网版权所有   联系电话:18123376007

备案号:ICP2024067431-1 川公网安备51140202000466号


本站为文档C2C交易模式,即用户上传的文档直接被用户下载,本站只是中间服务平台,本站所有文档下载所得的收益归上传人(含作者)所有。装配图网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对上载内容本身不做任何修改或编辑。若文档所含内容侵犯了您的版权或隐私,请立即通知装配图网,我们立即给予删除!