第2章 函数和作用_第1页
第2章 函数和作用_第2页
第2章 函数和作用_第3页
第2章 函数和作用_第4页
第2章 函数和作用_第5页
已阅读5页,还剩58页未读 继续免费阅读

下载本文档

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

文档简介

1、第2章 函数的定义 当设计较大程序时,通常需要用若干个模块来实当设计较大程序时,通常需要用若干个模块来实现较复杂的功能,而每一个模块自成结构,用来解决现较复杂的功能,而每一个模块自成结构,用来解决一些子问题。一些子问题。这种能完成某一独立功能的子程序模块,这种能完成某一独立功能的子程序模块,在在C+C+中成为函数。中成为函数。 可见,可见,函数是实现模块化程序的基本单位。函数是实现模块化程序的基本单位。 事实上,函数还能体现代码重用的思想,因为一事实上,函数还能体现代码重用的思想,因为一个函数可以在同一个程序中被多次调用或在多个程序个函数可以在同一个程序中被多次调用或在多个程序中被调用。中被调

2、用。第第2章章 函数和作用域函数和作用域2.1函数定义和调用函数定义和调用2.2C+函数特性函数特性2.3作用域和存储类型作用域和存储类型2.4名称空间名称空间2.1 函数定义和调用函数定义和调用2.1.1函数定义函数定义C+C+的任何一个程序都可由的任何一个程序都可由一个主函数一个主函数和和若干个子函数若干个子函数组合而成。主组合而成。主函数可以调用子函数,子函数还可以调用其他子函数。函数可以调用子函数,子函数还可以调用其他子函数。C+C+规定主函数名规定主函数名必须是必须是mainmain,而其他函数可以是,而其他函数可以是库函数库函数或或自定义函数自定义函数。(1 1)主函数)主函数ma

3、inmain不仅是程序的入口函数,而且与其他函数相比较还不仅是程序的入口函数,而且与其他函数相比较还有许多使用上的限制。例如,它不能被其他函数调用,不能用有许多使用上的限制。例如,它不能被其他函数调用,不能用inlineinline和和staticstatic来说明等。来说明等。(2 2)库函数,)库函数,又称标准函数,是又称标准函数,是ANSI/ISO C+ANSI/ISO C+编译系统已经预先定编译系统已经预先定义好的函数,程序设计时可根据实际需要,直接使用这类函数,而不必义好的函数,程序设计时可根据实际需要,直接使用这类函数,而不必重新定义。调用时,必须在程序中包含相应的头文件,并指明使

4、用名称重新定义。调用时,必须在程序中包含相应的头文件,并指明使用名称空间空间stdstd。2.1 函数定义和调用函数定义和调用 (3)自定义函数自定义函数是用户根据程序的需要,是用户根据程序的需要,将某一个功能相将某一个功能相对独立的程序定义成的一个函数,或将解决某个问题的算法用一对独立的程序定义成的一个函数,或将解决某个问题的算法用一个函数来组织。个函数来组织。 与变量的使用规则相同,在与变量的使用规则相同,在C+程序中一定要先说明和定义程序中一定要先说明和定义函数,然后才能调用函数。函数,然后才能调用函数。 C+中每一个函数的定义都是由中每一个函数的定义都是由4个个部分组成的,即部分组成的

5、,即函数名、函数类型、形式参数表和函数体,函数名、函数类型、形式参数表和函数体,其定其定义的格式如下:义的格式如下: ( ) 函函数数体体2.1.1 函数定义函数定义其中,函数名应是一个合法有效的其中,函数名应是一个合法有效的C+C+标识符;函数头的形式参数标识符;函数头的形式参数又简称为形参。又简称为形参。 参数表中的每一个形参都是由形参的数据类型和形参名来构成,参数表中的每一个形参都是由形参的数据类型和形参名来构成,根据上述定义格式,可以编写一个函数根据上述定义格式,可以编写一个函数sumsum,如图,如图2.12.1所示,注意它所示,注意它们的书写规范。们的书写规范。int sum(in

6、t x, int y)int z = x + y;return z;对齐函数头函数体缩进函数类型函数名形参图图2.1 定义一个函数定义一个函数sum2.1.1 函数定义函数定义需要说明的是:需要说明的是:(1)(1) C/C+ C/C+不允许在一个函数体中再定义函数,不允许在一个函数体中再定义函数,即禁止嵌套定义,但即禁止嵌套定义,但允许嵌套调用。允许嵌套调用。(2)(2) 函数体也可不含有任何语句,这样的函数称为函数体也可不含有任何语句,这样的函数称为空函数,它仅为空函数,它仅为程序结构而设定,本身没有任何操作。程序结构而设定,本身没有任何操作。(3)(3) 函数类型决定了函数所需要的返回值

7、类型,它可以是除数组类函数类型决定了函数所需要的返回值类型,它可以是除数组类型之外的任何有效的型之外的任何有效的C+C+数据类型,包括引用、指针等。数据类型,包括引用、指针等。2.1.1 函数定义函数定义(4) (4) 若函数类型为若函数类型为voidvoid时时,则表示该函数没有返回值。则表示该函数没有返回值。但仍然可以在但仍然可以在函数体中使用函数体中使用returnreturn语句语句“returnreturn ;”;”,此时可将,此时可将“return;”return;”语语句理解为是函数体花括号句理解为是函数体花括号“”“”的作用,当流程遇到函数体的的作用,当流程遇到函数体的“”“”

8、时,函数调用结束,控制权返回给主调函数。例如:时,函数调用结束,控制权返回给主调函数。例如:void f1( int a)if (a 10) return; / return;一旦执行,后面的语句不再一旦执行,后面的语句不再 被执行被执行当当a10条件满足时,条件满足时,“return;”语句将控制权返回给主调函数。语句将控制权返回给主调函数。2.1.2 函数的调用和声明函数的调用和声明1. 1. 函数的实参和形参函数的实参和形参定义一个函数就是为了以后的调用。调用函数时,先写函数名,然定义一个函数就是为了以后的调用。调用函数时,先写函数名,然后紧跟括号,括号里是后紧跟括号,括号里是实际调用该

9、函数时所给定的参数,称为实际参数,实际调用该函数时所给定的参数,称为实际参数,简称实参,简称实参,并与形参相对应。要注意形参和实参的区别:并与形参相对应。要注意形参和实参的区别:(1) (1) 从模块概念来说,从模块概念来说,形参是函数的接口,是存在于函数内部的变形参是函数的接口,是存在于函数内部的变量。而实参是存在于函数外部的变量。量。而实参是存在于函数外部的变量。它们不是同一个实体,也就是说,它们不是同一个实体,也就是说,形参变量和实参变量所对应的内存空间不是同一个内存空间。形参变量和实参变量所对应的内存空间不是同一个内存空间。(2) (2) 按函数定义时所指定的形参类型,实参除变量外还可

10、以是数值按函数定义时所指定的形参类型,实参除变量外还可以是数值或表达式等,而形参只能是变量。或表达式等,而形参只能是变量。(3) (3) 形参在函数调用之前是不存在的,只有在发生函数调用时,函形参在函数调用之前是不存在的,只有在发生函数调用时,函数中的形参才会被分配内存空间,然后执行函数体中的语句,而当调用数中的形参才会被分配内存空间,然后执行函数体中的语句,而当调用结束后,形参所占的内存空间又会被释放。结束后,形参所占的内存空间又会被释放。2.1.2 函数的调用和声明函数的调用和声明2. 2. 函数的调用函数的调用 函数调用的一般格式为:函数调用的一般格式为: ( ( ) ); 调用函数时要

11、注意:调用函数时要注意:实参与形参的个数应相等,实参与形参的个数应相等,类型应一致,且按顺序对应,一一传递数据。类型应一致,且按顺序对应,一一传递数据。 例如,下面的示例用来输出一个三角形的图案。例如,下面的示例用来输出一个三角形的图案。2.1.2 函数的调用和声明函数的调用和声明 例例Ex_Call 函数的调用函数的调用#include using namespace std;void printline( char ch, int n )for (int i = 0 ; in ; i+)coutch;coutendl ;int main()int row = 5;for (int i =

12、0; irow; i+)printline(*, i+1);/ Areturn 0; 2.1.2 函数的调用和声明函数的调用和声明 程序运行的结果如下:程序运行的结果如下: 代码中,代码中,main函数的函数的for循环语句共调用了循环语句共调用了5次次printline函数函数(A句句),每次调用时因实参,每次调用时因实参i+1值不断改变,从而使函数值不断改变,从而使函数printline打印打印出来的星号个数也随之改变出来的星号个数也随之改变。 2.1.2 函数的调用和声明函数的调用和声明3. 3. 函数的声明函数的声明 由于前面函数由于前面函数printline的定义代码是放在的定义代码

13、是放在main函函数中调用语句数中调用语句A之前,因而之前,因而A语句执行不会有问题。语句执行不会有问题。 但若将函数但若将函数printline的定义代码放在调用语句的定义代码放在调用语句A之之后,即后,即函数定义在后,而调用在前函数定义在后,而调用在前,就会产生,就会产生“printline标识符未定义标识符未定义”的编译错误。的编译错误。 此时此时必须在调用前进行函数声明必须在调用前进行函数声明。2.1.2 函数的声明函数的声明声明一个函数按下列格式进行:声明一个函数按下列格式进行: ( );可见,可见,函数声明的格式是在函数头的后面加上分号函数声明的格式是在函数头的后面加上分号“;”。

14、但要注意,函数声明的内容应和函数的定义应相但要注意,函数声明的内容应和函数的定义应相同。同。例如,对于前面例如,对于前面sum函数和最后一个函数和最后一个printline函数可有函数可有如下声明:如下声明:int sum(int x, int y);void printline( char ch, int n );2.1.3 值传递值传递 函数的调用实质上就是参数传递,函数的调用实质上就是参数传递, 在在C+中,函数的参数传递有两种方式,中,函数的参数传递有两种方式,一是按值传递,一是按值传递,二是地址传递或引用传递。二是地址传递或引用传递。 这里先来说明按值传递的参数传递方法,地址传递或引

15、这里先来说明按值传递的参数传递方法,地址传递或引用传递在以后来讨论。用传递在以后来讨论。 当函数的形参定义成一般变量时,如前面当函数的形参定义成一般变量时,如前面printline和和sum函数的形参都是一般变量,此时函数的参数传递就是按函数的形参都是一般变量,此时函数的参数传递就是按值传递方式,简称值传递方式,简称值传递值传递,是指当一个函数被调用时,是指当一个函数被调用时,C+根据实参和形参的对应关系将实际参数的值一一传递给形参,根据实参和形参的对应关系将实际参数的值一一传递给形参,供函数执行时使用。供函数执行时使用。2.1.3 值传递值传递值传递的特点是:值传递的特点是:n(1) 若实参

16、指定是若实参指定是一般变量一般变量,则传递的是,则传递的是实参变量的值实参变量的值而不是实参变量的地址。而不是实参变量的地址。n(2) 在执行函数代码时,由于对实参数据的操作最终是在执行函数代码时,由于对实参数据的操作最终是在形参的内存空间中进行,因此形参值的改变只是改在形参的内存空间中进行,因此形参值的改变只是改变了形参的内存空间存储的值,而不会改变实参变量变了形参的内存空间存储的值,而不会改变实参变量所对应的内存空间的值。所对应的内存空间的值。 也就是说,也就是说,即使形参的值在函数中发生了变化,函即使形参的值在函数中发生了变化,函数调用结束后,实参的值不会受到影响。数调用结束后,实参的值

17、不会受到影响。例如:例如:2.1.3 值传递值传递例例Ex_SwapValue 交换函数两个参数的值。交换函数两个参数的值。#include using namespace std;void swap(float x, float y);/ 函数原型说明函数原型说明int main()float a = 20, b = 40;couta = a, b = bn;swap(a, b);/ 函数调用函数调用couta = a, b = bn;return 0;void swap(float x, float y)/ 函数定义函数定义float temp;temp = x; x = y; y = t

18、emp;coutx = x, y = yn;2.1.3 值传递值传递程序的运行结果为:程序的运行结果为: 可以看出,虽然函数swap中交换了两个形参x和y的值,但交换的结果并不能改变实参的值,所以调用该函数后,变量a和b的值仍然为原来的值。2.1.4 函数的默认形参值函数的默认形参值在在C+中,中,允许在函数的声明或定义时给一个或多个参允许在函数的声明或定义时给一个或多个参数指定默认值。数指定默认值。这样在调用时,可以不给出实际参数,而这样在调用时,可以不给出实际参数,而按指定的默认值进行工作。按指定的默认值进行工作。例如:例如:void delay(int loops = 1000) / 函

19、数定义,函数定义,1000为形为形 参参loops的默认值的默认值if ( 0 = loops) return;for (int i=0; iloops; i+); / 空循环,起延时作用空循环,起延时作用2.1.4 函数的默认形参值函数的默认形参值这样,当有调用这样,当有调用 delay();/ 和和delay(1000)等效等效 程序就会自动将程序就会自动将loops当作成当作成1000的默认值来进行处理。的默认值来进行处理。当然,也可在当然,也可在函数调用时指定相应的实际的参数值函数调用时指定相应的实际的参数值,例如:例如:delay(2000);/ 形参形参loops的值为的值为200

20、02.1.4 函数的默认形参值函数的默认形参值在设置函数的默认形参值时要注意:在设置函数的默认形参值时要注意:(1) 当函数既有原型声明又有定义时当函数既有原型声明又有定义时,默认参数只能在原型声明中指,默认参数只能在原型声明中指定,而不能在函数定义中指定。定,而不能在函数定义中指定。例如:例如:void delay(int loops); / 函数原型声明函数原型声明/ void delay(int loops = 1000) / 错误:此时不能函数定义中指定默错误:此时不能函数定义中指定默认参数认参数/ 2.1.4 函数的默认形参值函数的默认形参值(2) 当一个函数中需要有多个默认参数时,

21、当一个函数中需要有多个默认参数时,则形参分布中,默认参数应则形参分布中,默认参数应严格从右到左逐次定义和指定,中间不能跳开严格从右到左逐次定义和指定,中间不能跳开。例如:例如:void display(int a, int b, int c = 3);/ 合法合法void display(int a, int b = 2, int c = 3); / 合法合法void display(int a = 1, int b = 2, int c = 3); / 合法:可以对所有的参合法:可以对所有的参数设置默认值数设置默认值void display(int a, int b = 2, int c);

22、 / 错误:默认参数应从最错误:默认参数应从最右边开始右边开始void display(int a = 1, int b = 2, int c);/ 错误:默认参数应从最错误:默认参数应从最右边开始右边开始void display(int a = 1, int b, int c = 3); / 错误:多个默认参数错误:多个默认参数中间不能有非默认参数中间不能有非默认参数2.1.4 函数的默认形参值函数的默认形参值n(3) (3) 当带有默认参数的函数调用时,当带有默认参数的函数调用时,系统按从左到右系统按从左到右的顺序将实参与形参结合。的顺序将实参与形参结合。 当实参的数目不足时,系统将按同样

23、的顺序用声当实参的数目不足时,系统将按同样的顺序用声明或定义中的默认值来补齐所缺少的参数。明或定义中的默认值来补齐所缺少的参数。 例如:例如:2.1.4 函数的默认形参值函数的默认形参值例例Ex_Default 在函数定义中设置多个默认参数在函数定义中设置多个默认参数#include using namespace std;void display(int a, int b = 2, int c = 3) / 在函数定义中设置默认在函数定义中设置默认参数参数couta = a, b = b, c = cn;int main()display(1);display(1, 5);display(1

24、, 7, 9);return 0;程序的运行结果为:程序的运行结果为:2.2 C+函数特性函数特性 在在C+C+中,函数还有:中,函数还有:嵌套调用、重载、内联调用嵌套调用、重载、内联调用以及递归调用以及递归调用等特性,相应的函数被称为嵌套函数、等特性,相应的函数被称为嵌套函数、重载函数、内联函数和递归调用等。重载函数、内联函数和递归调用等。2.2 C+函数特性函数特性 函数重载(函数重载(overloaded)是是C+对对C的扩展,它允许多的扩展,它允许多个同名的函数存在,但同名的各个函数的形参必须有区别:个同名的函数存在,但同名的各个函数的形参必须有区别:要么形参的个数不同;要么形参的个数

25、相同,但参数类型要么形参的个数不同;要么形参的个数相同,但参数类型有所不同有所不同。 优点:代码中使用函数的重载,不仅方便函数名的记优点:代码中使用函数的重载,不仅方便函数名的记忆,而且更主要的是完善了同一个函数的代码功能,给调忆,而且更主要的是完善了同一个函数的代码功能,给调用带来了许多方便。用带来了许多方便。下例程序中即是各种形式的下例程序中即是各种形式的sum函数都称为函数都称为sum的重载的重载函数。函数。 2.2 C+函数特性函数特性【例【例Ex_OverLoad】 编程求两个或三编程求两个或三个操作数之和个操作数之和 #include int sum(int x, int y);

26、int sum(int x, int y, int z); double sum(double x, double y); double sum(double x, double y, double z); /声明声明4个同名的函数个同名的函数 int main() coutsum(2, 5)endl; / 结果为结果为7 coutsum(2, 5, 7)endl; / 结果为结果为14 coutsum(1.2, 5.0, 7.5)0当时当时2.2.3 递归函数递归函数例例Ex_Factorial 编程求编程求n的阶乘的阶乘n! #include using namespace std; lo

27、ng factorial(int n); int main() coutfactorial(4)endl; / 结果为结果为24 return 0; long factorial(int n) long result = 0; if (0 = n) result = 1; else result = n*factorial(n-1); / 进行自身调用进行自身调用 return result; 1.5.4 函数的递归调用函数的递归调用程序运行结果如下:程序运行结果如下:24下面来分析下面来分析main函数中函数中“factorial(4);”语句的执行过程,这一过程用语句的执行过程,这一过程用

28、 图图1.8来表示:见书(来表示:见书(p36)2.2.4 内联函数内联函数 在程序的执行过程中,调用函数时首先需要保存主调函数的现场和返回地址,然后程序转移到被调函数的起始地址继续执行。被调函数执行结束后,先恢复主调函数的现场,取出返回地址,并将返回值赋给函数调用本身,最后在返回地址处开始继续执行。当函数体比较小且执行的功能比较简单时,这种函数调用方式的系统开销相对较大。 2.2.4 内联函数内联函数 为了解决这一问题,C+引入了内联函数的概念,它把函数体的代码直接插入到调用处,将调用函数的方式改为顺序执行、直接插入的程序代码,这样可以减少程序的执行时间,但同时增加了代码的实际长度。 内联函

29、数的使用方法与一般函数相同,只是在内联函数定义时,需在函数的类型前面加上inline关键字关键字。例如:2.2.4 内联函数内联函数例例Ex_Inline 用内联函数实现求两个实数的最大值用内联函数实现求两个实数的最大值 #include using namespace std; inline float fmax(float x, float y) return xy?x:y; int main() float a; a = fmax(5, 10); / A cout最大的数为:最大的数为:a10 ? 5 : 10;2.2.4 内联函数内联函数在使用内联函数时,还需要注意的是:在使用内联函数

30、时,还需要注意的是:(1) 内联函数也要遵循定义在前,调用在后的原则。形参与实参之间内联函数也要遵循定义在前,调用在后的原则。形参与实参之间的关系与一般函数相同。的关系与一般函数相同。(2) 关键字关键字inline必须放在函数定义体前才是内联函数,仅在函数声必须放在函数定义体前才是内联函数,仅在函数声明时使用明时使用inline,不能定义内联函数。,不能定义内联函数。(3) 在在C+中,需要定义成的内联函数不能含有循环、中,需要定义成的内联函数不能含有循环、switch和复杂和复杂嵌套的嵌套的if语句。语句。(4) 递归函数不能被用来做内联函数。递归函数不能被用来做内联函数。 总之,内联函数

31、一般是比较小的、经常被调用的、大多可在一总之,内联函数一般是比较小的、经常被调用的、大多可在一行写完的函数。行写完的函数。2.3 作用域和存储类型作用域和存储类型2.3.1作用域作用域 作用域又称作用范围,是指程序中标识符作用域又称作用范围,是指程序中标识符(变量名、函数名、数组变量名、函数名、数组名、类名、对象名等名、类名、对象名等)的有效范围。的有效范围。一个标识符是否可以被引用,称之一个标识符是否可以被引用,称之为标识符的可见性。为标识符的可见性。 在一个在一个C+程序项目中,一个标识符只能在声明或定义它的范围程序项目中,一个标识符只能在声明或定义它的范围内可见,在此之外是不可见的。内可

32、见,在此之外是不可见的。 根据标识符的作用范围,可将其作用域分为根据标识符的作用范围,可将其作用域分为5种:种:函数原型作用域、函数原型作用域、函数作用域、块作用域、类作用域和文件作用域函数作用域、块作用域、类作用域和文件作用域。 其中,类作用域将在以后介绍,这里介绍其他几种。其中,类作用域将在以后介绍,这里介绍其他几种。2.3 作用域和存储类型作用域和存储类型n1. 函数原型作用域函数原型作用域 函数原型作用域指的是函数原型作用域指的是在声明函数原型所指定的参数标识符在声明函数原型所指定的参数标识符的作用范围。的作用范围。 这个这个作用范围是在函数原型声明中的左、右圆括号之间。作用范围是在函

33、数原型声明中的左、右圆括号之间。 正因为如此,在函数原型中声明的标识符可以与函数定义中正因为如此,在函数原型中声明的标识符可以与函数定义中说明的标识符名称不同。由于所声明的标识符与该函数的定义及说明的标识符名称不同。由于所声明的标识符与该函数的定义及调用无关,所以可以在函数原型声明中只作参数的类型声明,而调用无关,所以可以在函数原型声明中只作参数的类型声明,而省略参数名。省略参数名。 例如:例如: double max(double x, double y); 和和 double max(double, double); 是等价的。是等价的。2.3.1 作用域作用域2 块作用域块作用域块语句就

34、是由块语句就是由“”和和“”组成的组成的(复合语句复合语句)。在块中声明的标识符,。在块中声明的标识符,其作用域从声明处开始,一直到结束块的花括号为止。其作用域从声明处开始,一直到结束块的花括号为止。块作用域也称作块作用域也称作局部作用域局部作用域,具有块作用域的变量是,具有块作用域的变量是局部变量局部变量。例如:。例如:void fun(void) a的作用域的作用域b的作用域的作用域int a;/ a的作用域起始处的作用域起始处cina;if (a0) a = -a;int b;/ b的作用域起始处的作用域起始处 / b的作用域终止处的作用域终止处 / a的作用域终止处的作用域终止处2.3

35、.1 作用域作用域需要说明的是:需要说明的是:当标识符的作用域完全相同时,不允许出现相同的当标识符的作用域完全相同时,不允许出现相同的标识符名。而当标识符具有不同的作用域时,却允许标识符同名。标识符名。而当标识符具有不同的作用域时,却允许标识符同名。例如:例如:void fun(void)块块A块块B/ 块块A开始开始int i;/ 块块B开始开始int i;i = 100;/ 块块B结束结束/ 块块A结束结束 代码中,在代码中,在A和和B块中都声明了变量块中都声明了变量i,这是允许的,因为块,这是允许的,因为块A和块和块B不是同一个作用域。不是同一个作用域。块块A块块B2.3.1 作用域作用

36、域 但同时出现另外一个问题,语句但同时出现另外一个问题,语句“i = 100;”中的中的i是使用是使用A块块中的变量中的变量i还是使用还是使用B中的变量中的变量i? C+规定在这种作用域嵌套的情况下,规定在这种作用域嵌套的情况下,如果内层如果内层(块块B)和外层和外层(块块A)作用域声明了同名的标识符,那么在外层作用域中声明的标作用域声明了同名的标识符,那么在外层作用域中声明的标识符对于该内层作用域是不可见的。识符对于该内层作用域是不可见的。 也就是说,在块也就是说,在块B声明的变量声明的变量i与块与块A声明的变量声明的变量i无关,当块无关,当块B中的中的i=100时,不会影响块时,不会影响块

37、A中变量中变量i的值。的值。2.3.1 作用域作用域4. 文件作用域文件作用域 在所有函数外定义的标识符称为在所有函数外定义的标识符称为全局标识符全局标识符。全局标识符的作用。全局标识符的作用域是文件作用域。域是文件作用域。它从声明之处开始,直到文件结束一直是可见的它从声明之处开始,直到文件结束一直是可见的。具有文件作用域。具有文件作用域的变量和常量称为全局变量和全局常量。的变量和常量称为全局变量和全局常量。例如:例如:const float PI = 3.14;/ 全局常量全局常量PI,其作用域从此开始,其作用域从此开始int a;/ 全局变量全局变量a,其作用域从此开始,其作用域从此开始v

38、oid main( ) / void funA(int x) / 2.3.1 作用域作用域5 函数作用域函数作用域若函数定义在后,调用在前,必须进行函数原型声明。若函数定义在若函数定义在后,调用在前,必须进行函数原型声明。若函数定义在前,调用在后,函数定义包含了函数的原型声明。前,调用在后,函数定义包含了函数的原型声明。 一旦声明了函数原型,函数标识符的作用域是从声明或定义之处开一旦声明了函数原型,函数标识符的作用域是从声明或定义之处开始到源程序文件结束。始到源程序文件结束。例如:例如:void funA(int x ); / 函数函数funA的作用域从此开始到文件结束的作用域从此开始到文件结

39、束void funB( ) / 函数函数funB的作用域从此开始到文件结束的作用域从此开始到文件结束/ void main( ) / void funA(int x) / 2.3.3 存储类型存储类型 存储类型是针对变量而言的,它规定了变量的生存期。无论是全局存储类型是针对变量而言的,它规定了变量的生存期。无论是全局变量还是局部变量,编译系统往往变量还是局部变量,编译系统往往根据其存储方式定义、分配和释放相根据其存储方式定义、分配和释放相应的内存空间。应的内存空间。变量的存储类型变量的存储类型反映了变量在哪开辟内存空间以及占用内存空间的反映了变量在哪开辟内存空间以及占用内存空间的有效期限。有效

40、期限。在在C+中,变量有中,变量有4种存储类型:种存储类型:自动类型、静态类型、寄存器类自动类型、静态类型、寄存器类型和外部类型,型和外部类型,这些存储类型是在变量定义时来指定的这些存储类型是在变量定义时来指定的.其一般格式如下:其一般格式如下: ; 2.3.3 存储类型存储类型1. 自动存储类型 一般说来,用自动存储类型声明的变量都是限制在某个程序范围内使用,即为局部变量。 从系统角度来说,自动存储类型变量是采用动态分配方式在栈区中来分配内存空间。因此,当程序执行到超出该变量的作用域时,就释放它所占用的内存空间,其值也随之消失了。 在C+语言中,声明一个自动存储类型的变量是在变量类型前加上关

41、键字auto, 例如: autoint i;2.3.3 存储类型存储类型 若自动存储类型的变量是在函数内或语句块中声若自动存储类型的变量是在函数内或语句块中声明的,则可省略关键字明的,则可省略关键字auto,例如:,例如:void fun()int i;/ 省略省略auto/ 2.3.3 存储类型存储类型2 寄存器类型寄存器类型使用使用关键字关键字register声明寄存器类型声明寄存器类型的变量的目的是将所的变量的目的是将所声明的变量放入寄存器内,从而加快程序的运行速度。声明的变量放入寄存器内,从而加快程序的运行速度。例如:例如:register int i;/ 声明寄存器类型变量声明寄存器

42、类型变量 但有时,在使用但有时,在使用register声明时,若系统寄存器已经被声明时,若系统寄存器已经被其他数据占据时,寄存器类型的变量就会自动当作其他数据占据时,寄存器类型的变量就会自动当作auto变量。变量。2.3.3 存储类型存储类型2. 静态类型 从变量的生存期来说,一个变量的存储空间可以是永久的,即在程序运行期间该变量一直存在,如全局变量。 也可以是临时的,如局部变量,当流程执行到它的说明语句时,系统为其在栈区中动态分配一个临时的内存空间,并在它的作用域中有效,一旦流程超出该变量的作用域时,就释放它所占用的内存空间,其值也随之消失。 2.3.3 存储类型存储类型 但是,若在声明局部

43、变量类型前面加上关键字static,则将其定义成了一个静态类型的变量。 这样的变量虽具有局部变量的作用域,但由于它是用静态分配方式在静态数据区中来分配内存空间。 因此,在这种方式下,只要程序还在继续执行,静态类型变量的值就一直有效,不会随它所在的函数或语句块的结束而消失。 简单地说,静态类型的局部变量虽具有局部变量的作用域,但却有全局变量的生存期。2.3.3 存储类型存储类型例例Ex_Static 使用静态类型的局部变量使用静态类型的局部变量#include using namespace std;void count()int i = 0;static int j = 0; / 静态类型静态

44、类型i+;j+;couti = i, j = jn;int main()count();count();return 0; 2.3.3 存储类型存储类型 程序中,当第程序中,当第1次调用函数次调用函数count时,由于变量时,由于变量j是静态类型,因此其是静态类型,因此其初值设为初值设为0后不再进行初始化,执行后不再进行初始化,执行j+后,后,j值为值为1,并一直有效。第,并一直有效。第2次次调用函数调用函数count时,由于时,由于j已分配内存且进行过初始化,因此语句已分配内存且进行过初始化,因此语句“static int j = 0;”被跳过,执行被跳过,执行j+后,后,j值为值为2。故程

45、序运行结果为:故程序运行结果为: 2.3.3 存储类型存储类型 需要说明的是,静态类型的局部变量只在第一次执行时进行初始化,正因为如此,在声明静态类型变量时一定要指定其初值,若没有指定,编译器还会将其初值置为0。2.4 名称空间名称空间 随着程序代码的增多,名称相互冲突的可能性也将增加。尤其是在程序中使用多家厂商提供的类库时,标示符名称的冲突可能性更高。 例如,两个类库中可能都定义了名为List、Tree和Node的类,但定义的含义和方式不兼容;或是两个类库定义基个程序本相同,但两个类库同时包含在一个程序中,会出现标示符重复定义的错误。这些问题统称为名称空间问题。 解决这些名称空间问题的方法是

46、使用C+新的名称空间特性,通过名称空间的作用域机制来解决。2.4 名称空间名称空间2.4.1 名称空间的定义 在C+中,定义一个名称空间的格式如下: 其中,namespace是C+关键字,标识符用作名称空间的名称,属于该名称空间体中的变量、函数、结构、枚举、联合以及以后要讨论的类等都可以认为是该名称空间的成员。namespace 标识符标识符成员;成员;体体2.4.1 名称空间的定义名称空间的定义需要说明的是:需要说明的是:同一个文件中,可以允许定义多个名称空间。一旦定义名称空间后,同一个文件中,可以允许定义多个名称空间。一旦定义名称空间后,标识符就标识名称空间体的那个区域。标识符就标识名称空

47、间体的那个区域。例如:例如:using namespace std;namespace DING1char name = this is in DING1 region!;void showname( void )coutnameendl;2.4.1 名称空间的定义名称空间的定义namespace DING2char name = this is in DING2 region!;void showname( void )coutnameendl; 尽管名称空间DIN1和DING2中定义的成员名都相同,但它们由于分属不同的名称区域,因而是合法的。 此后,在引用各自的成员时就可使用域作用运算符来指定DIN1或DING2来标识各自所在的名称区域。2.4.1 名称空间的定义名

温馨提示

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

评论

0/150

提交评论