第4章函数和预处理_第1页
第4章函数和预处理_第2页
第4章函数和预处理_第3页
第4章函数和预处理_第4页
第4章函数和预处理_第5页
已阅读5页,还剩50页未读 继续免费阅读

下载本文档

版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领

文档简介

1、第第4 4章章 函数和预处理函数和预处理 在结构化程序设计中,函数是一个十分重要的在结构化程序设计中,函数是一个十分重要的概念,它有利于代码共享,节省开发时间,增强程概念,它有利于代码共享,节省开发时间,增强程序的可靠性。序的可靠性。 mainfun1fun2fun11fun12fun13fun21fun22软件结构图示例软件结构图示例4.1 4.1 函函 数数 概概 述述 所谓函数就是一系列指令或语句的组合体。设计函所谓函数就是一系列指令或语句的组合体。设计函数有两个目的:数有两个目的: (1)模块化程序设计。)模块化程序设计。 (2)代码重用,使程序简洁清晰。)代码重用,使程序简洁清晰。

2、图图4.1是调用函数的基本流程图。从图中看到,当是调用函数的基本流程图。从图中看到,当一个程序在调用某个函数时,一个程序在调用某个函数时,C+会自动转移到被调会自动转移到被调用的函数中执行,执行完后再回到原先程序执行的位用的函数中执行,执行完后再回到原先程序执行的位置,然后继续执行下一条语句。置,然后继续执行下一条语句。 调调用用函函数数 1 调调用用函函数数 2 函函数数 1 函函数数 2 图图4.1 4.1 函数调用图函数调用图 4.2 4.2 函数的定义和调用函数的定义和调用 在使用函数时,要先对函数进行定义,确定它要实在使用函数时,要先对函数进行定义,确定它要实现的功能。函数的使用就是

3、调用函数的过程。现的功能。函数的使用就是调用函数的过程。4.2.1 函数定义函数定义 C+中每一个函数的定义都是由中每一个函数的定义都是由4个部分组成的,个部分组成的,即函数类型、函数名、函数参数表和函数体,其形式即函数类型、函数名、函数参数表和函数体,其形式如下:如下: 函数类型函数类型 函数名函数名(函数参数表函数参数表) 函数体函数体; 下面是一个函数的例子,它返回两个整数中较大者。下面是一个函数的例子,它返回两个整数中较大者。函数有两个形参函数有两个形参x和和y,返回值类型为,返回值类型为int: int max(int x, int y) return (xy ? x : y); 函

4、数定义中的形参是指调用此函数所需要的参数个函数定义中的形参是指调用此函数所需要的参数个数和类型。一般地,仅当函数被调用时,系统才会给数和类型。一般地,仅当函数被调用时,系统才会给形参分配内存单元,而调用结束后,形参所占用的内形参分配内存单元,而调用结束后,形参所占用的内存单元又被释放。存单元又被释放。4.2.2 函数的说明函数的说明 定义一个函数就是为了以后的调用,但如果函数定义一个函数就是为了以后的调用,但如果函数定义在后而调用在前,就会产生编译错误。定义在后而调用在前,就会产生编译错误。 为此,必须将函数定义在前或者在调用前进行为此,必须将函数定义在前或者在调用前进行“函函数的说明数的说明

5、”。函数说明消除了函数定义的位置影响。函数说明消除了函数定义的位置影响。 也就是说,不管函数是在何处定义的,只要在调用也就是说,不管函数是在何处定义的,只要在调用前进行函数的说明,就可保证函数调用的合法性。前进行函数的说明,就可保证函数调用的合法性。 虽然函数不一定在程序的开始就说明,但为了提高虽然函数不一定在程序的开始就说明,但为了提高程序的可读性和保证简洁的程序结构,最好将主函数程序的可读性和保证简洁的程序结构,最好将主函数main放在程序的开头,而将函数说明放在主函数放在程序的开头,而将函数说明放在主函数main之前。之前。 说明一个函数的格式如下:说明一个函数的格式如下: 函数类型函数

6、类型 函数名函数名(函数参数表函数参数表); 其中,其中,“函数类型函数类型”指出函数返回值的类型;指出函数返回值的类型; “函函数名数名”指出被说明的函数的名称;指出被说明的函数的名称; “函数参数表函数参数表”说说明该函数的形参,如同函数定义一样,明该函数的形参,如同函数定义一样, “函数参数表函数参数表”中的参数个数可以为中的参数个数可以为0,但圆括号不能省略。,但圆括号不能省略。 在说明函数时,在说明函数时,“函数参数表函数参数表”有两种方式:一种有两种方式:一种与函数定义时的与函数定义时的“函数参数表函数参数表”一样,给出形参名和对一样,给出形参名和对应的数据类型的列表,另一种方式是

7、只给出形参的数据应的数据类型的列表,另一种方式是只给出形参的数据类型的列表。例如,对于前面定义的类型的列表。例如,对于前面定义的max函数,以下两函数,以下两个说明语句是等同的:个说明语句是等同的: int max(int x,int y); int max(int,int); 注意:在说明函数时,不要忘记末尾的分号注意:在说明函数时,不要忘记末尾的分号“;”。 4.2.3 函数的调用函数的调用 调用函数时,在函数名后跟参数(调用函数时的参调用函数时,在函数名后跟参数(调用函数时的参数称为实际参数或实参),参数用逗号隔开。调用时,数称为实际参数或实参),参数用逗号隔开。调用时,将实参中的实参值

8、逐个代入形参,这一过程称为向函将实参中的实参值逐个代入形参,这一过程称为向函数传递参数。函数调用的一般形式如下:数传递参数。函数调用的一般形式如下: 函数名函数名(实际参数表实际参数表);例如:例如:c=max(2,10); 注意:实参与形参的个数应相等、类型应一致,且注意:实参与形参的个数应相等、类型应一致,且按顺序一一对应传递数据。按顺序一一对应传递数据。4.3 4.3 函数的参数传递函数的参数传递 C+C+中函数的参数传递有按值传递、地址传递和引中函数的参数传递有按值传递、地址传递和引用传递,由于地址也是一种值,所以,按值传递和地用传递,由于地址也是一种值,所以,按值传递和地址传递都是单

9、向的值传递方式。址传递都是单向的值传递方式。 1按值传递按值传递 所谓按值传递,是指当一个函数被调用时,所谓按值传递,是指当一个函数被调用时,C+根根据实参和形参的对应关系将实际参数值一一传递给形据实参和形参的对应关系将实际参数值一一传递给形参,供函数执行时使用。参,供函数执行时使用。 函数本身不对实参进行操作,也就是说,即使形参函数本身不对实参进行操作,也就是说,即使形参的值在函数中发生了变化,实参的值不会受到影响。的值在函数中发生了变化,实参的值不会受到影响。这样的参数,也称为传值参数。这样的参数,也称为传值参数。【例【例1】 分析以下程序的执行结果。分析以下程序的执行结果。#includ

10、e void swap(int,int);/函数说明函数说明void main() int a=2,b=10; swap(a,b); cout a= a , b= b endl;void swap(int x,int y) /函数定义函数定义 int temp=x; x=y; y=temp; 执行结果:执行结果: a=2, b=10 2地址传递地址传递 所谓地址传递,是指在函数定义时将形参说明成所谓地址传递,是指在函数定义时将形参说明成指针,这样调用函数时就需要指定地址值形式的实参。指针,这样调用函数时就需要指定地址值形式的实参。 这种地址传递与前面的按值传递不同,它的实现机这种地址传递与前面

11、的按值传递不同,它的实现机制是让形参的指针直接指向实参,在被调用的函数中制是让形参的指针直接指向实参,在被调用的函数中可以通过改变形参指针所指的实参变量值来间接改变可以通过改变形参指针所指的实参变量值来间接改变实参值。实参值。 地址传递方式的特点是可以通过改变形参所指向地址传递方式的特点是可以通过改变形参所指向的变量来影响实参。这样的参数称为传址参数。的变量来影响实参。这样的参数称为传址参数。【例】【例】 分析以下程序的执行结果。分析以下程序的执行结果。#include void swap(int *,int *);void main() int a=2,b=10; swap(&a,&

12、amp;b); /传递传递a和和b的地址值的地址值 cout a= a ,b= b endl;void swap(int *x,int *y) int temp=*x; /这里的这里的“*”符号表示取指针指向的值符号表示取指针指向的值 *x=*y; *y=temp; 执行结果:执行结果: a=10,b=2 3引用传递引用传递 在讨论引用传递之前先来解释在讨论引用传递之前先来解释“引用引用”的概念。简的概念。简单地说,单地说,“引用引用”实际上是给一个已知变量起个别名,实际上是给一个已知变量起个别名,对引用的操作也就是对被它引用的变量的操作。对引用的操作也就是对被它引用的变量的操作。 C+中的变

13、量名对应着内存的存储位置,可以使中的变量名对应着内存的存储位置,可以使用一个特定内存地址访问到它。引用则引入了变量的用一个特定内存地址访问到它。引用则引入了变量的另一个名字(别名),它和这个变量具有同一内存地另一个名字(别名),它和这个变量具有同一内存地址。定义引用的方式是:址。定义引用的方式是: 类型类型 &引用名引用名=已定义变量名已定义变量名例如:例如: int val; int &refval=val; /refval是是val的引用的引用 这里引用这里引用refval和和val的使用几乎完全一样。不同的的使用几乎完全一样。不同的是,引用必须进行初始化,而且一旦初始化后

14、,就不是,引用必须进行初始化,而且一旦初始化后,就不能再成为其他变量的别名。可以为任意类型的变量创能再成为其他变量的别名。可以为任意类型的变量创建引用。建引用。 引用使用基本上和一般变量一样,例如:引用使用基本上和一般变量一样,例如: int i; int &ri=i; ri=3;/这时这时i的值也变为的值也变为3了了 i=4;/这时这时ri的值也为的值也为4 使用引用时应注意几点:使用引用时应注意几点: 引用不是变量,它的值不能改变。引用不是变量,它的值不能改变。 引用不是指针,不能用于分配动态内存。引用不是指针,不能用于分配动态内存。 引用的目的在于可以用较清晰的方法编写改变引用的

15、目的在于可以用较清晰的方法编写改变其参数值的函数,以及接受结构和对象作为参数的函数。其参数值的函数,以及接受结构和对象作为参数的函数。 引用的最大用途是当一个函数返回一个引用时,引用的最大用途是当一个函数返回一个引用时,它可以成为左值。它可以成为左值。 一个函数能使用引用传递方式是在函数定义时一个函数能使用引用传递方式是在函数定义时将形参前加上引用运算符将形参前加上引用运算符“&”。 【例】【例】 分析以下程序的执行结果。分析以下程序的执行结果。#include void swap(int &,int &);void main() int a=2,b=10; swap(

16、a,b); cout a= a ,b= b endl;void swap(int &x,int &y) int temp=x; x=y; y=temp; 执行结果:执行结果: a=10,b=2 4函数的默认参数值函数的默认参数值 在在C+中,允许在函数的说明或定义时给一个或多中,允许在函数的说明或定义时给一个或多个参数指定默认值。这样在调用时,可以不给出参数,个参数指定默认值。这样在调用时,可以不给出参数,而按指定的默认值进行工作。而按指定的默认值进行工作。 例如,以下是带默认参数值的函数说明:例如,以下是带默认参数值的函数说明: void initialize(int pri

17、ntNo, int state=0); 该函数将指定的打印机初始化为指定状态。其中第该函数将指定的打印机初始化为指定状态。其中第一个参数一个参数printNo表示打印机号码,第二个参数表示打印机号码,第二个参数state表表示打印机状态。若调用时没有给定第二个参数,系统示打印机状态。若调用时没有给定第二个参数,系统会自动将打印机的初始化状态设为会自动将打印机的初始化状态设为0。在调用上述函数。在调用上述函数时可以用以下几种语句:时可以用以下几种语句: initialize(1);/初始化初始化1号打印机状态为号打印机状态为0 initialize(1,0);/效果同上效果同上 initiali

18、ze(1,1);/初始化初始化1号打印机状态为号打印机状态为1 函数可以将其全部或部分参数说明为带默认值,但函数可以将其全部或部分参数说明为带默认值,但带默认值的参数只能放在参数表的最后。这是因为,系带默认值的参数只能放在参数表的最后。这是因为,系统进行参数匹配时是依照从前往后的顺序,如果中间参统进行参数匹配时是依照从前往后的顺序,如果中间参数有默认值,它就无法判断哪些参数使用默认值。数有默认值,它就无法判断哪些参数使用默认值。 例如,以下函数说明不正确:例如,以下函数说明不正确: f(int par1=1,int par2,int par3=3); f(int par1=1,int par2

19、=2,int par3); f(int par1,int par=2,int par3); 程序还可以通过重新说明函数使本来不带默认值的程序还可以通过重新说明函数使本来不带默认值的参数带上默认值,这是使通用函数特定化的有效方法。参数带上默认值,这是使通用函数特定化的有效方法。但需注意:在一个文件中,函数的某一参数只能在一次但需注意:在一个文件中,函数的某一参数只能在一次说明中指定默认值。说明中指定默认值。 【例】【例】 分析以下程序的执行结果。分析以下程序的执行结果。#include void initialize(int printNo,int state=0);void initializ

20、e(int printNo=1,int state); /重新说明重新说明printNo默认值默认值void main() initialize(); initialize(0); initialize(1,1);void initialize(int printNo,int state) cout printNo= printNo ,; cout state= state endl; 程序结果:程序结果:printNo=1,state=0printNo=0,state=0printNo=1,state=14.4 4.4 内内 联联 函函 数数 内联(内联(inline)是内联扩展()是内联扩

21、展(inline expansion)的简)的简称。与一般函数不同,称。与一般函数不同,C+编译器在遇到调用内联函数编译器在遇到调用内联函数的地方会用函数体中的代码来替换函数的调用,好处是的地方会用函数体中的代码来替换函数的调用,好处是节省函数调用带来的参数传递、运行栈的入栈与出栈等节省函数调用带来的参数传递、运行栈的入栈与出栈等开销,从而提高运行速度,但付出的代价是增加了代码开销,从而提高运行速度,但付出的代价是增加了代码长度。内联函数的定义是在一般函数定义前加上长度。内联函数的定义是在一般函数定义前加上inline关键字。关键字。 例如,以下的程序定义了一个内联函数例如,以下的程序定义了一

22、个内联函数abs(): inline int abs(int x) if (x1 前者称为递归出口,后者称为递归体。前者称为递归出口,后者称为递归体。 【例】采用递归方法求【例】采用递归方法求1+2+100。递归模型:递归模型:f(1)=1 n=1f(n)=n+f(n-1) n1程序如下:程序如下:int fun(int n) if (n=1) return(1) else return(n+fun(n-1);void main() printf(“%dn”,fun(100);4.6 4.6 函函 数数 重重 载载 所谓函数重载是指同一个函数名可以对应多个函数所谓函数重载是指同一个函数名可以对

23、应多个函数的实现。例如,可以给函数名的实现。例如,可以给函数名add()定义多个函数实现,定义多个函数实现,该函数的功能是求和,即求两个运算数的和。其中,该函数的功能是求和,即求两个运算数的和。其中,一个函数实现是求两个一个函数实现是求两个int型数之和,另一个实现是求型数之和,另一个实现是求两个浮点型数之和,再一个实现是求两个复数的和。两个浮点型数之和,再一个实现是求两个复数的和。每种实现对应着一个函数体,这些函数的名字相同,每种实现对应着一个函数体,这些函数的名字相同,但是函数的参数类型不同。这就是函数重载的概念。但是函数的参数类型不同。这就是函数重载的概念。 函数重载要求编译器能够惟一地

24、确定调用一个函数函数重载要求编译器能够惟一地确定调用一个函数时应执行哪个函数代码,即采用哪个函数实现。确定时应执行哪个函数代码,即采用哪个函数实现。确定函数实现时,要求从函数参数的个数和类型上来区分。函数实现时,要求从函数参数的个数和类型上来区分。这就是说,进行函数重载时,要求同名函数在参数个这就是说,进行函数重载时,要求同名函数在参数个数上不同,或者参数类型上不同。否则,将无法实现数上不同,或者参数类型上不同。否则,将无法实现重载。重载。【例】以下程序是参数类型不同的重载函数的实现。【例】以下程序是参数类型不同的重载函数的实现。#include int abs(int x) return x

25、0 ? x : -x;double abs(double x) return x0 ? x : -x;void main() cout -10的绝对值是的绝对值是: abs(-10) endl; cout -123.45的绝对值是的绝对值是: abs(-123.45) endl; abs()函数重载的概念如下图所示。体现函数重载的概念如下图所示。体现动态链接即多态性的概念。动态链接即多态性的概念。 调用调用 abs 函数函数 C+接口接口 求整求整数绝数绝对值对值abs 函函数数 求浮求浮点数点数绝对绝对值值 abs函数函数 abs()函数重载函数重载 【例】【例】 以下程序是参数个数不相同的

26、重载函数的实现。以下程序是参数个数不相同的重载函数的实现。#include int min(int a,int b) return ab ? a : b; int min(int a,int b,int c) int t=min(a,b); return min(t,c);int min(int a,int b,int c,int d) int t1=min(a,b); int t2=min(c,d); return min(t1,t2); void main() cout min(13,5,4,9) endl; cout min(-2,8,0) end;4.7 4.7 作作 用用 域域 变量

27、的作用域是指程序中变量有效的区域,它是变量的作用域是指程序中变量有效的区域,它是变量的一个重要特性。变量的作用域分为三类:文件变量的一个重要特性。变量的作用域分为三类:文件域、局部域和类域。域、局部域和类域。 文件域也称全局域,指一个程序文件中除函数和文件域也称全局域,指一个程序文件中除函数和类定义以外的部分,它是程序中最外层的域。一个在类定义以外的部分,它是程序中最外层的域。一个在全局域中说明的变量从说明开始到文件结束都是有效全局域中说明的变量从说明开始到文件结束都是有效的;的; 局部域一般是指函数定义的程序范围,也可以是局部域一般是指函数定义的程序范围,也可以是函数中具有说明语句的复合语句

28、(即块结构)。定义函数中具有说明语句的复合语句(即块结构)。定义在局部域中的变量只在该域有效;在局部域中的变量只在该域有效; 类域类似于局部域,一个类定义就是一个类域。类域类似于局部域,一个类定义就是一个类域。 教材中图教材中图4.4的例子说明了除类域外的其他种类的域的例子说明了除类域外的其他种类的域范围。范围。 4.7.1 永久变量、临时变量和静态变量永久变量、临时变量和静态变量 相对于程序执行的生命周期来说,变量的存储空相对于程序执行的生命周期来说,变量的存储空间可以是永久的,即在程序运行期间该变量一直存在;间可以是永久的,即在程序运行期间该变量一直存在;也可以是暂时的,即变量在程序运行到

29、达其作用域时也可以是暂时的,即变量在程序运行到达其作用域时才会产生,而作用域结束时,变量也随之消亡。才会产生,而作用域结束时,变量也随之消亡。 全局变量是永久的,它们在程序运行的过程中一直全局变量是永久的,它们在程序运行的过程中一直存在;而局部变量大多是临时的,它们只在说明它们存在;而局部变量大多是临时的,它们只在说明它们的作用域内发挥作用,一旦程序控制离开了这一作用的作用域内发挥作用,一旦程序控制离开了这一作用域,这些局部变量所占空间就会释放。域,这些局部变量所占空间就会释放。 有一种方法可以让局部变量既有在局部域作用的特有一种方法可以让局部变量既有在局部域作用的特性(即变量只能在变量作用的

30、范围内被访问),又可性(即变量只能在变量作用的范围内被访问),又可以永久存在,这就是将局部变量说明为是静态的。将以永久存在,这就是将局部变量说明为是静态的。将变量说明为是静态的方式是在变量定义前加上变量说明为是静态的方式是在变量定义前加上static标标记,例如:记,例如: static int local; 系统给说明为静态的局部变量分配固定的存储空系统给说明为静态的局部变量分配固定的存储空间,而不是每次执行到该局部域时才分配空间,所间,而不是每次执行到该局部域时才分配空间,所以它能一直保持值。以它能一直保持值。 静态局部变量只在第一次执行时初始化一次,而静态局部变量只在第一次执行时初始化一

31、次,而一般的局部变量每次执行到该局部域都需要初始化。一般的局部变量每次执行到该局部域都需要初始化。全局变量可以显式初始化,也可以由系统隐式初始全局变量可以显式初始化,也可以由系统隐式初始化为化为0。 全局变量也可以是静态的,定义方式与局部变量全局变量也可以是静态的,定义方式与局部变量一样,也是在定义前加上一样,也是在定义前加上static。不过,静态全局变。不过,静态全局变量的含义完全不同于静态局部变量:被定义为静态量的含义完全不同于静态局部变量:被定义为静态的全局变量只在定义它的文件中可见,其他文件中的全局变量只在定义它的文件中可见,其他文件中不能使用。静态全局变量有两个好处:一是信息隐不能

32、使用。静态全局变量有两个好处:一是信息隐藏,二是可以在不同的文件中使用意义不同而同名藏,二是可以在不同的文件中使用意义不同而同名的变量名。的变量名。 【例】以下程序的输出结果是【例】以下程序的输出结果是 。 #include int f() static int i=0;int s=1;s+=i;i+;return s; main() int i,a=0;for(i=0;i5;i+) a+=f();printf(%dn,a); 答:答:f函数中的函数中的i为静态变量,第为静态变量,第1次调用时次调用时i置初值置初值0,退出该函数,退出该函数时时i不会释放存储空间,以后再调不会释放存储空间,以后

33、再调用用f时,不再给时,不再给i置初值,直接使置初值,直接使用其以前的结果。本题答案为:用其以前的结果。本题答案为:15。【例】以下列程序的输出结果是【例】以下列程序的输出结果是 。#include f(int a)int b=0;static int c=3;a=c+,b+;return(a);main()int a=2,i,k;for(i=0;i2;i+)k=f(a+);printf(%dn,k); 答:答:f函数中的函数中的c为静态变量,第为静态变量,第1次调用时次调用时c置初值置初值3,退出该函数,退出该函数时时c不会释放存储空间,以后再调不会释放存储空间,以后再调用用f时,不再给时,

34、不再给c置初值,直接使用置初值,直接使用其以前的结果。本题答案为:其以前的结果。本题答案为:4。 【例】编写一个函数【例】编写一个函数fun,累计当前人数和总分,累计当前人数和总分,并返回当前为止的平均分。调用该函数输出并返回当前为止的平均分。调用该函数输出10个学生个学生的平均分统计结果。的平均分统计结果。 解:函数解:函数fun以当前学生分数为参数,设计两个静以当前学生分数为参数,设计两个静态变量态变量n和和s,分别累加人数和总分,返回,分别累加人数和总分,返回s/n。对应的。对应的程序如下:程序如下: #include float fun(int m) static int n=0;st

35、atic float s=0;s+=m;/*累计总分累计总分*/n+;/*累计人数累计人数*/return(s/n); void main() int a=80,56,76,92,85,63,76,96,72,78;int n=10,i;printf(序号序号 分数分数 平均分平均分n);for (i=0;i-4.7.2 域运算符域运算符 局部变量可以隐藏全局变量,那么在有同名全局部变量可以隐藏全局变量,那么在有同名全局和局部变量的情形时如何访问全局变量呢局和局部变量的情形时如何访问全局变量呢?域运算域运算符符“:”可以提供对全局变量的访问。以域运算符为可以提供对全局变量的访问。以域运算符为前

36、缀的变量表示全局变量。前缀的变量表示全局变量。 例如,下面程序中有两个变量同名,一个为全局例如,下面程序中有两个变量同名,一个为全局变量,一个为局部变量,可以通过域运算符访问全变量,一个为局部变量,可以通过域运算符访问全局变量:局变量: int var=10; /全局变量全局变量 func() int var; /局部变量局部变量 var=:var; /将全局变量的值赋给局部变量将全局变量的值赋给局部变量 4.7.3 外部变量外部变量 静态全局变量只能在定义它的文件中使用,那么非静静态全局变量只能在定义它的文件中使用,那么非静态全局变量呢态全局变量呢?定义在文件定义在文件1中的非静态全局变量可

37、以由中的非静态全局变量可以由另一个文件(文件另一个文件(文件2)引用,但需要在文件)引用,但需要在文件2中说明一下中说明一下所要引用的变量,该变量对于文件所要引用的变量,该变量对于文件2来说是外部变量。来说是外部变量。外部变量说明时用外部变量说明时用extern标识。标识。 例如,在文件例如,在文件1中定义了一个非静态全局变量中定义了一个非静态全局变量var,需,需要在文件要在文件2中使用它的值,则需要在文件中使用它的值,则需要在文件2中使用以下语中使用以下语句说明这个外部变量:句说明这个外部变量: extern int var; 这样,告诉编译器,它不是一个新的变量,该变量这样,告诉编译器,

38、它不是一个新的变量,该变量已定义过,直接使用它的值即可。在一个应用程序中,已定义过,直接使用它的值即可。在一个应用程序中,变量的定义只能一次,而说明可以多次。编译程序在遇变量的定义只能一次,而说明可以多次。编译程序在遇到变量说明语句时,不再给它分配存储空间。到变量说明语句时,不再给它分配存储空间。 和变量相同,定义在一个文件中的函数也可以由和变量相同,定义在一个文件中的函数也可以由另一个文件引用,这时也可以使用另一个文件引用,这时也可以使用extern说明,例如,说明,例如,以下语句说明一个外部函数以下语句说明一个外部函数fun(): extern void func(int,int); 注意

39、:在说明外部变量时不能给变量赋初值,否则注意:在说明外部变量时不能给变量赋初值,否则出错。出错。 4.7.4 自动变量和寄存器变量自动变量和寄存器变量 C+提供了静态变量、外部变量、自动变量和寄提供了静态变量、外部变量、自动变量和寄存器变量等四种存储类型,这些存储类型的说明是按存器变量等四种存储类型,这些存储类型的说明是按下列格式进行的:下列格式进行的: 存储类型的关键字存储类型的关键字 类型名类型名 变量名表变量名表; ; 其中,其中,“存储类型的关键字存储类型的关键字”指出变量的存储类型。指出变量的存储类型。前面已经介绍了静态变量和外部变量,下面再讨论自前面已经介绍了静态变量和外部变量,下

40、面再讨论自动变量和寄存器变量。动变量和寄存器变量。1自动变量(自动变量(auto) 一般说来,用自动存储类型说明的变量都限制在某一般说来,用自动存储类型说明的变量都限制在某个程序范围内使用,即为局部变量。从系统角度来说,个程序范围内使用,即为局部变量。从系统角度来说,自动存储类型变量是采用堆栈方式分配内存空间。因自动存储类型变量是采用堆栈方式分配内存空间。因此,当程序执行到超出该变量的作用域时,就释放它此,当程序执行到超出该变量的作用域时,就释放它所占用的内存空间,其值也随之消失了。所占用的内存空间,其值也随之消失了。 在在C+语言中,说明一个自动存储类型的变量是在语言中,说明一个自动存储类型

41、的变量是在变量类型前面加上关键字变量类型前面加上关键字auto,例如:,例如: auto int n; 若自动存储类型的变量是在函数内或语句块中说明若自动存储类型的变量是在函数内或语句块中说明的,则可省略关键字的,则可省略关键字auto。其实,我们在程序中见到。其实,我们在程序中见到的未说明存储类型的变量都是的未说明存储类型的变量都是auto变量。变量。2寄存器变量(寄存器变量(register) 使用关键字使用关键字register说明寄存器类型的变量的目说明寄存器类型的变量的目的是将所说明的变量放入寄存器内,从而加快程序的是将所说明的变量放入寄存器内,从而加快程序的运行速度。的运行速度。

42、但有时在使用这种说明时,若系统寄存器已经但有时在使用这种说明时,若系统寄存器已经被其他数据占用,寄存器类型的变量就会自动当作被其他数据占用,寄存器类型的变量就会自动当作auto变量。变量。 4.8 4.8 文件与预处理文件与预处理 C+程序的源代码中可包含各种编译指令,这些程序的源代码中可包含各种编译指令,这些指令称为预处理命令。虽然它们实际上不是指令称为预处理命令。虽然它们实际上不是C+语语言的一部分,但扩展了言的一部分,但扩展了C+程序设计的环境。程序设计的环境。 C+提供的预处理命令主要有以下三种:提供的预处理命令主要有以下三种: 宏定义命令宏定义命令 文件包含命令文件包含命令 条件编译

43、命令条件编译命令 这些命令在程序中都是以这些命令在程序中都是以“#”来引导,每一条预来引导,每一条预处理命令必须单独占用一行;由于它不是处理命令必须单独占用一行;由于它不是C+的语的语句,因此一般在结尾没有分号句,因此一般在结尾没有分号“;”。4.8.1 宏定义命令宏定义命令 宏是一种编译预处理命令,根据是否带参数分为宏是一种编译预处理命令,根据是否带参数分为无参宏和带参宏。无参宏和带参宏。 1. 无参宏无参宏 无参宏定义语句的一般格式如下:无参宏定义语句的一般格式如下: #define 标识符标识符 字符串字符串 无参宏用一个简单的名称代替一个长的字符串。这无参宏用一个简单的名称代替一个长的

44、字符串。这个标识符称为个标识符称为“宏名宏名”,在预编译时将宏名替换成字,在预编译时将宏名替换成字符串的过程序称为符串的过程序称为“宏展开宏展开”或或“宏替换宏替换”。 宏名习惯上用大写字母表示,以与变量名相区别。宏名习惯上用大写字母表示,以与变量名相区别。宏定义是用宏名代替一个字符串,只作简单的置换,宏定义是用宏名代替一个字符串,只作简单的置换,不作语法检查。不作语法检查。 #define命令出现在程序中所有函数的外面,宏名命令出现在程序中所有函数的外面,宏名的有效范围是定义命令之后到本文件结束,但可用的有效范围是定义命令之后到本文件结束,但可用#undef命令终止宏定义的作用域。命令终止宏

45、定义的作用域。 例如:例如: #define MAX 1002. 带参宏带参宏 带参宏定义语句的一般格式如下:带参宏定义语句的一般格式如下: #define 标识符标识符(标识符标识符1,标识符标识符2,标识符标识符n) 字符串字符串 其中,括号中的标识符表是形式参数。对带参宏的其中,括号中的标识符表是形式参数。对带参宏的展开也是用字符串代替宏名,但是其中的形式参数要展开也是用字符串代替宏名,但是其中的形式参数要用相应的实际参数代替。用相应的实际参数代替。 例如,以下语句定义了一个求正方形面积的宏:例如,以下语句定义了一个求正方形面积的宏: #define area(a) (a)*(a) 为什

46、么要加上括号呢?这是因为为什么要加上括号呢?这是因为“替换替换”是简单的替是简单的替换操作,当换操作,当area的实际参数是一个表达式时,不加括的实际参数是一个表达式时,不加括号会在编译时出错。如:号会在编译时出错。如: #define area(a) (a*a) 当调用当调用area(2+3)时,替换成:时,替换成: (2+3*2+3)那么,求出的面积是那么,求出的面积是11,而不是正确的,而不是正确的25。 带参宏和函数在形式和使用上都很相似。例如,以带参宏和函数在形式和使用上都很相似。例如,以下语句定义了一个求两个数中较大值的宏:下语句定义了一个求两个数中较大值的宏: #define m

47、ax(a,b) (a)(b)?(a):(b)【例】下列正确的预编译命令是【例】下列正确的预编译命令是 。A.define PI 3.14159B.#define P(a,b) strcpy(a,b)C.#define stdio.hD.#define PI 3.14159; 答:选项答:选项A中应用中应用#define。选项。选项C标识符和字标识符和字符串之间不应为符串之间不应为“.”。选项。选项D不应以不应以“;”结尾。本结尾。本题答案为:题答案为:B。 【例】设有宏定义【例】设有宏定义“#define AREA(a,b) a*b”,则正,则正确的确的“宏调用宏调用”是是 。A.s=AREA

48、(r*r)B.s=AREA(x*y)C.s=AREAD.s=c*AREA(x=3.5),(y+4.1) 答:选项答:选项A、B、C都存在参数错误。本题答案为:都存在参数错误。本题答案为:D。4.8.2 文件包含命令文件包含命令 所谓所谓“文件包含文件包含”是指将另一个源文件的内容合是指将另一个源文件的内容合并到当前源程序中。并到当前源程序中。C+语言提供了语言提供了#include命令用命令用来实现文件包含操作,它有下列两种格式:来实现文件包含操作,它有下列两种格式: #include #include 文件名文件名 文件名一般是以文件名一般是以.h为扩展名,因而称它为为扩展名,因而称它为“头文头文件件”,如前面的程序中,如前面的程序中iostream.h是头文件的文件名。是头文件的文件名。文件包含的两种格式中,第一种格式是将文件名用尖文件包含的两种格式中,第一种格式是将文件名用尖括号括号“”括起来,用来包含那些由系统提供的并放括起来,用来包含那些由系统提供的并放在指定子目录中的头文件;第二种格式是将文件名用在指定子目录中的头文件;第二种格式是将文件名用双引号括起来,用来包含那些由用户自己定义的

温馨提示

  • 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
  • 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
  • 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
  • 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
  • 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
  • 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
  • 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。

最新文档

评论

0/150

提交评论