Presentation is loading. Please wait.

Presentation is loading. Please wait.

授课老师:龚涛 信息科学与技术学院 2017年3月 教材:《Visual C++程序员成长攻略》 《C++ Builder程序员成长攻略》

Similar presentations


Presentation on theme: "授课老师:龚涛 信息科学与技术学院 2017年3月 教材:《Visual C++程序员成长攻略》 《C++ Builder程序员成长攻略》"— Presentation transcript:

1 授课老师:龚涛 信息科学与技术学院 2017年3月 教材:《Visual C++程序员成长攻略》 《C++ Builder程序员成长攻略》

2 第4章 函数和作用域 函数的定义和说明 函数的调用 函数的参数 内联函数 函数重载 函数的嵌套调用和递归调用 作用域 C++语言的系统函数
第4章 函数和作用域 函数的定义和说明 函数的调用 函数的参数 内联函数 函数重载 函数的嵌套调用和递归调用 作用域 C++语言的系统函数 东华大学信息科学与技术学院 龚涛

3 第4章 函数和作用域 4.1 函数的定义和说明 例4.1 求两个浮点数之和的程序。
第4章 函数和作用域 4.1 函数的定义和说明 函数是C++语言的基本特征,封装了一些程序代码和数据,实现了更高级的抽象。在C++语言编程中常把一个程序分成多个函数来实现,这样既能实现更高级的抽象,还能实现参数化和结构化。 例4.1 求两个浮点数之和的程序。 #include <iostream.h> void main() { double x, y; cout<<"Input double x and y:"; cin>>x>>y; double z=x+y; cout<<"sum="<<z<<endl; } double sum_double() { double x, y; cout<<"Input double x and y:"; cin>>x>>y; double s=x+y; retun s; } 东华大学信息科学与技术学院 龚涛

4 4.1 函数的定义和说明 例4.1 求两个浮点数之和的程序。
4.1 函数的定义和说明 例4.1 求两个浮点数之和的程序。 可以说标识符(sum_double)是用来对若干条语句序列的抽象,这种抽象称为函数,而标识符(sum)称为函数名。对sum_double()函数的操作数也可以进行抽象,即实现参数化。该程序中,引入函数的目的在于求出两个浮点数的和,因此函数执行完后要返回一个值,以更新主函数中sum变量的值。 #include <iostream.h> double sum_double(double x, double y) { return x+y; } void main() double a, b; cout<<"Input double a and b:"; cin>>a>>b; double sum=sum_double(a,b); cout<<"sum="<<sum<<endl; 东华大学信息科学与技术学院 龚涛

5 4.1 函数的定义和说明 4.1.1 函数的定义格式 在C++语言中定义一个函数的格式如下:
4.1 函数的定义和说明 函数的定义格式 在C++语言中定义一个函数的格式如下: <类型> <函数名>(<参数表>) { <若干条语句> } 其中,<类型>是该函数的类型,即为该函数返回值的类型。它包含数据类型和存储类,可以是各种数据类型,包含基本数据类型和构造数据类型,也包含指针和引用类型。存储类通常省略,表示为外部函数。 函数头包括<类型>、<函数名>和<参数表>。 函数体用花括号将<若干条语句>括起来。 东华大学信息科学与技术学院 龚涛

6 在C++语言中,函数的说明(声明)原则如下: 如果一个函数定义在先,调用在后,则调用前可以不必说明;
4.1 函数的定义和说明 函数的说明方法 在C++语言中,函数的说明(声明)原则如下: 如果一个函数定义在先,调用在后,则调用前可以不必说明; 如果一个函数定义在后,调用在先,则调用前必须说明。 说明函数的方法如下: <类型> <函数名>(<参数表>); 为了避免确定函数定义的顺序,并且为了使得程序设计的逻辑结构更加清楚,常常将主函数放在程序头,这样就需要对被调用的函数进行说明。 东华大学信息科学与技术学院 龚涛

7 4.1 函数的定义和说明 例4.2 分析该程序的输出结果。 void fun2() {
4.1 函数的定义和说明 例4.2 分析该程序的输出结果。 void fun2() { cout<<"It’s in fun2."<<endl; fun1(); cout<<"It’s back in fun2."<<endl; } void fun3() cout<<"It’s in fun3."<<endl; #include <iostream.h> void fun1(), fun2(), fun3(); void main() { cout<<"It is in main."<<endl; fun2(); cout<<"It is back in main."<<endl; } void fun1() cout<<"It’s in fun1."<<endl; fun3(); cout<<"It’s back in fun1."<<endl; 东华大学信息科学与技术学院 龚涛

8 4.1 函数的定义和说明 例4.2 分析该程序的输出结果。 void fun2() {
4.1 函数的定义和说明 例4.2 分析该程序的输出结果。 void fun2() { cout<<"It’s in fun2."<<endl; fun1(); cout<<"It’s back in fun2."<<endl; } void main() cout<<"It is in main."<<endl; fun2(); cout<<"It is back in main."<<endl; #include <iostream.h> void fun3() { cout<<"It’s in fun3."<<endl; } void fun1() cout<<"It’s in fun1."<<endl; fun3(); cout<<"It’s back in fun1."<<endl; 东华大学信息科学与技术学院 龚涛

9 第4章 函数和作用域 4.2 函数的调用 4.2.1 函数的值和类型 函数的调用是用一个表达式来表示的,其调用格式如下:
第4章 函数和作用域 4.2 函数的调用 C++语言中不仅有传值调用(包含传变量地址值的传址调用),而且还有C语言中没有的引用调用。另外,C++语言中还允许设置形参的默认值等。 函数的值和类型 函数的调用是用一个表达式来表示的,其调用格式如下: <函数名>(<实参表>) 其中,<实参表>是由0个、1个或多个实在参数组成的,多个参数用逗号分隔,每个参数是一个表达式。实参的个数由形参决定,实参是用来在调用函数时给形参初始化的。因此,在调用函数时,实参的个数和类型要与形参的个数和类型是一致的,即数目相等,类型相同。 函数调用是一种表达式,函数调用表达式的值是函数的返回值,其类型是函数类型。 东华大学信息科学与技术学院 龚涛

10 函数的返回值是在被调用函数中通过返回语句来实现的,返回语句有两种格式。 格式一: return <表达式>; 格式二:
4.2 函数的调用 函数的值和类型 函数的返回值是在被调用函数中通过返回语句来实现的,返回语句有两种格式。 格式一: return <表达式>; 格式二: return; 格式一用于带有返回值的被调用函数中,被调用函数返回的值就是返回语句后面的表达式的值。 东华大学信息科学与技术学院 龚涛

11 具有<表达式>的返回语句实现过程如下: 先计算出<表达式>的值。
4.2 函数的调用 函数的值和类型 具有<表达式>的返回语句实现过程如下: 先计算出<表达式>的值。 如果<表达式>的类型与函数的类型不相同,则将表达式的类型自动转换为函数的类型,这种转换是强制性的(隐式强制转换),可能出现不保值(失真)的情况。 将计算出的表达式值返回给调用函数作为调用函数的值。该值可以赋给某变量,也可以直接显示输出。 将程序执行的控制权由被调用函数转向调用函数,执行调用函数后面的语句。 东华大学信息科学与技术学院 龚涛

12 4.2 函数的调用 4.2.2 函数的传值调用 函数调用方式在C++语言中除了传值调用之外,还有引用调用。
4.2 函数的调用 函数的传值调用 函数调用方式在C++语言中除了传值调用之外,还有引用调用。 在C++语言中根据变量值的不用类型,传值调用分为两种方式:一种是传递变量本身的值,称为传值调用;另一种是传递变量地址的值,称为传址调用。 传值调用的实现机制和特点 使用传值调用方式时,调用函数的实参用常量、变量值或表达式值,被调用函数的形参用变量。调用时系统先计算实参表达式的值,再将实参的值按位置对应赋给形参,即对形参进行初始化。因此,传值调用的实现机制是将实参复制一个副本给形参。传值调用的特点是形参值的改变不影响实参,参数传递的开销较大。 东华大学信息科学与技术学院 龚涛

13 4.2 函数的调用 4.2.2 函数的传值调用 传址调用的实现机制和特点
4.2 函数的调用 函数的传值调用 传址调用的实现机制和特点 使用传址调用方式时,调用函数的实参用地址值,被调用函数的形参用指针。调用时系统将实参的地址值赋给对应的形参指针,使形参指针指向实参变量。因此,传址调用与前述传值调用不同,它的实现机制是让形参指针所指向的实参变量值来间接改变实参值。 传址调用的特点是可以通过改变形参所指向的变量值来影响实参,这也是函数之间传递信息的一种手段。传递调用时只传递地址值,而不复制副本,因此参数传递的开销较小。 东华大学信息科学与技术学院 龚涛

14 4.2 函数的调用 4.2.3 函数的引用调用 引用调用是C++语言中的一种函数调用方式,C语言中没有这种调用方式。
4.2 函数的调用 函数的引用调用 引用调用是C++语言中的一种函数调用方式,C语言中没有这种调用方式。 引用是给一个已知变量起个别名,对引用的操作也就是对被它引用的变量的操作。引用主要是用来作为函数的形参和函数的返回值。 使用引用作为函数形参时,调用函数的实参要用变量名,将实参变量名赋给形参的引用,相当于在被调用函数中使用了实参的别名。 在被调用函数中,对引用的改变,就是直接通过引用来改变实参的变量值。这种调用具有传址调用中改变实参值和减少开销的特点,但它又比传址调用更方便、更直接。因此,在C++语言中常常使用引用作为函数形参在被调用函数中改变调用函数的实参值。 东华大学信息科学与技术学院 龚涛

15 在C++语言中,经常使用引用调用来实现函数之间的信息变换,因为这样做更方便、更容易,还易于维护。
4.2 函数的调用 函数的引用调用 在C++语言中,经常使用引用调用来实现函数之间的信息变换,因为这样做更方便、更容易,还易于维护。 在C++语言中,经常使用的是传值调用和引用调用,较少使用传址调用。因为传址调用要用到指针,而指针用来传递参数容易出错,所以,应尽量使用引用调用来替代传址调用,从而避免了指针的使用。 引用作为函数类型时,函数的返回值不是一个数值,而是一个变量的引用。 东华大学信息科学与技术学院 龚涛

16 克服这种二义性的方法是改变add_int()函数的两个实参的写法,尽量避免二义性的出现。
第4章 函数和作用域 4.3 函数的参数 函数参数的求值顺序 当一个函数带有多个参数时,C++语言没有规定在函数调用时实参的求值顺序。编译器根据对代码进行优化的需要自行规定对实参的求值顺序,有的编译器规定自左至右,有的编译器规定自右至左。这种对求值顺序的不同规定,对一般参数来讲没有影响。但是,如果实参表达式中带有副作用的运算符,就有可能由于求值顺序不同而产生二义性。 克服这种二义性的方法是改变add_int()函数的两个实参的写法,尽量避免二义性的出现。 东华大学信息科学与技术学院 龚涛

17 在C++语言中,允许在函数说明或定义时给一个或多个参数指定默认值。 但是,要求在一个指定了默认值的参数的右边,不能出现没有指定默认值的参数。
4.3 函数的参数 设置函数参数的默认值 在C++语言中,允许在函数说明或定义时给一个或多个参数指定默认值。 但是,要求在一个指定了默认值的参数的右边,不能出现没有指定默认值的参数。 在函数调用时,编译器按从左至右的顺序将实参与形参结合。当实参的数目不足时,编译器将按同样的顺序使用说明或定义中的默认值来补足所缺少的实参。 在给某个参数指定默认值时,不仅可以是一个数值,而且还可以是任意复杂的表达式。 东华大学信息科学与技术学院 龚涛

18 该程序中,在说明函数add_int()时,给函数参数设置了默认值,而其中一个参数的值被设置为一个已知变量(m)的值。
设置函数参数的默认值 例4.9 分析下列程序的输出结果。 该程序中,在说明函数add_int()时,给函数参数设置了默认值,而其中一个参数的值被设置为一个已知变量(m)的值。 通常,在当一个函数有定义又有说明时,参数的默认值要在说明时设置。 #include <iostream.h> int m(8); int add_int(int x, int y=7, int z=m); void main() { int a(5), b(15), c(20); int s=add_int(a,b); cout<<s<<endl; } int add_int(int x, int y, int z) return x+y+z; 东华大学信息科学与技术学院 龚涛

19 实际应用中,形参和实参中一个用指针,另一个用数组也是可以的。在使用指针时可以用数组名,也可以用另外定义的指向数组元素的指针。
4.3 函数的参数 使用数组作为函数参数 数组作为函数参数可以分为如下三种情况。 形参和实参都用数组 调用函数的实参用数组名,被调用函数的形参也用数组名。这种调用机制是形参和实参共用内存中的同一个数组。因此,在被调用函数中改变了数组中某个元素的值,对调用函数该数组中的相应元素值也被改变。 形参和实参都用对数组的指针 实际应用中,形参和实参中一个用指针,另一个用数组也是可以的。在使用指针时可以用数组名,也可以用另外定义的指向数组元素的指针。 实参用数组名形参用引用 东华大学信息科学与技术学院 龚涛

20 该程序中,开始定义了一个外部的一维数组a,给它的前7个元素赋了值,这时a[7]的值为默认值0。
使用数组作为函数参数 例4.10 分析下列程序的输出结果。 该程序中,开始定义了一个外部的一维数组a,给它的前7个元素赋了值,这时a[7]的值为默认值0。 在main()中,输出数组a的元素a[7]的值,理应为b[7]的值,因为它们共用数组。 #include <iostream.h> int a[8]={1,3,5,7,9,11,13}; void fun(int b[], int n) { for(int i=0;i<n-1;i++) b[7]+=b[i]; } void main() int m=8; fun(a,m); cout<<a[7]<<endl; 东华大学信息科学与技术学院 龚涛

21 4.3.3 使用数组作为函数参数 例4.11 分析下列程序的输出结果。
使用数组作为函数参数 例4.11 分析下列程序的输出结果。 该程序中,定义的函数fun()的形参pa是一个指向int型变量的指针,本程序让它指向数组a的首元素。因此,在调用表达式fun(a,m)中,a是数组a[8]的数组名,它是一个地址值。 #include <iostream.h> int a[8]={1,3,5,7,9,11,13}; void fun(int *pa, int n) { for(int i=0;i<n-1;i++) *(pa+7)+=*(pa+i); } void main() int m=8; fun(a,m); cout<<a[7]<<endl; 东华大学信息科学与技术学院 龚涛

22 4.3.3 使用数组作为函数参数 例4.11 分析下列程序的输出结果。 在main()中,增加一条说明语句: int *p=a;
使用数组作为函数参数 例4.11 分析下列程序的输出结果。 在main()中,增加一条说明语句: int *p=a; 并将调用表达式fun(a,m);改为: fun(p,m); #include <iostream.h> int a[8]={1,3,5,7,9,11,13}; void fun(int *pa, int n) { for(int i=0;i<n-1;i++) *(pa+7)+=*(pa+i); } void main() int m=8; int *p=a; fun(p,m); cout<<a[7]<<endl; 东华大学信息科学与技术学院 龚涛

23 该程序中,在fun()函数中,使用了引用作为形参,调用时所对应的实参应该是一个数组名,这里的引用是给数组起个别名。
使用数组作为函数参数 例4.12 分析下列程序的输出结果。 该程序中,在fun()函数中,使用了引用作为形参,调用时所对应的实参应该是一个数组名,这里的引用是给数组起个别名。 #include <iostream.h> typedef int array[8]; int a[8]={1,3,5,7,9,11,13}; void fun(array &b, int n) { for(int i=0;i<n-1;i++) b[7]+=b[i]; } void main() int m=8; fun(a,m); cout<<a[7]<<endl; 东华大学信息科学与技术学院 龚涛

24 第4章 函数和作用域 4.4 内联函数 4.4.1 内联函数引入的原因
第4章 函数和作用域 4.4 内联函数 内联函数引入的原因 引入内联函数的目的是为了解决程序中函数调用的效率问题。函数的引入使得程序员只需关心函数的功能和使用方法,而不必关心函数功能的具体实现;另外,函数的引入可以减少程序的目标代码,实现程序代码和数据的共享。 但是,函数调用会降低效率,因为调用函数实际上是将程序执行顺序转移到函数的内存地址;将函数的程序内容执行完后,再转回到转去执行该函数前的地方。这种转移操作要求在转去前保护现场并记忆执行的地址,转回后先要恢复现场,并按原来保存的地址继续执行。因此,函数调用要消耗一定的时间和空间,这将影响其效率。特别是对于频繁被调用的函数来说,这种影响尤其明显。内联函数就是为了解决这一问题。 在程序编译时,编译器将程序中出现的内联函数的调用表达式用内联函数的函数体来进行替换。这种做法不会增加时间的开销,但会增加目标程序的代码量,进而增加空间开销。内联函数是以目标代码的增加为代价来换取时间的节省。 东华大学信息科学与技术学院 龚涛

25 4.4 内联函数 4.4.2 内联函数的定义方法 定义内联函数的方法是在函数定义的前面加上关键字inline。
4.4 内联函数 内联函数的定义方法 定义内联函数的方法是在函数定义的前面加上关键字inline。 例4.13 编程求出1~10中各个数的平方。 #include <iostream.h> inline int power_int(int x) { return (x)*(x); } void main() for(int i=1;i<=10;i++) int p=power_int(i); cout<<i<<"*"<<i<<"="<<p<<endl; 东华大学信息科学与技术学院 龚涛

26 4.4 内联函数 使用内联函数应注意的事项 内联函数具有一般函数的特性,它与一般函数的不同之处仅在于对函数调用的处理。一般函数进行调用时,要将程序的执行权转到被调用的函数中,然后再返回到调用它的函数中;而内联函数在进行调用时,是将调用表达式用内联函数体来替换。 在使用内联函数时,应注意如下几点: 在内联函数内不允许用循环语句和开关语句,否则按非内联函数处理。 内联函数定义必须出现在内联函数第一次被调用之前。 类结构中所有在类说明内部定义的函数都是内联函数。 东华大学信息科学与技术学院 龚涛

27 第4章 函数和作用域 4.5 函数重载 所谓函数重载是指同一个函数名可以对应着多个函数的实现。函数重载要求编译器能够唯一确定调用一个函数时应执行哪个函数代码,即采用哪个函数实现。确定函数实现必须从函数参数的个数和/或类型上区分。 参数类型上不同的重载函数 例4.14中,main()函数中调用相同名字add的两个函数,前面一个add()函数对应的是两个int型数求和的函数实现,后面一个add()函数对应的是两个double型数求和的函数实现。 东华大学信息科学与技术学院 龚涛

28 4.5 函数重载 4.5.1 参数类型上不同的重载函数 例4.14 求两个操作数之和。
4.5 函数重载 参数类型上不同的重载函数 例4.14 求两个操作数之和。 #include <iostream.h> int add(int, int); double add(double, double); void main() { cout<<add(5,10)<<endl; cout<<add(5.0,10.5)<<endl; } int add(int x, int y) return x+y; double add(double a, double b) return a+b; 东华大学信息科学与技术学院 龚涛

29 4.5 函数重载 4.5.2 参数个数上不同的重载函数 例4.15 找出几个int型数中的最小者。
4.5 函数重载 参数个数上不同的重载函数 例4.15 找出几个int型数中的最小者。 #include <iostream.h> int min(int a, int b); int min(int a, int b, int c); int min(int a, int b, int c, int d); void main() { cout<<min(13,5,4,9)<<endl; cout<<min(-2,8,0); } int min(int a, int b) return a<b?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); 东华大学信息科学与技术学院 龚涛

30 第4章 函数和作用域 4.6 函数的嵌套调用和递归调用 4.6.1 函数的嵌套调用
第4章 函数和作用域 4.6 函数的嵌套调用和递归调用 函数的嵌套调用 C++程序中允许函数的嵌套调用,所谓嵌套调用指的是在调用A函数的过程中,可以调用B函数;在调用B函数的过程中,还可以调用C函数等等。当C函数调用结束后,返回到B函数;当B函数调用结束后,再返回到A函数。例4.2就是函数嵌套调用的一个例子,下面给出函数嵌套调用的具体执行过程。 main() fun2() fun1() fun3() (1) (2) (3) (4) (5) (6) 调fun2() 调fun1() 调fun3() (7) (12) (11) (10) (9) (8) (13) 结束 东华大学信息科学与技术学院 龚涛

31 4.6 函数的嵌套调用和递归调用 函数的递归调用 在C++语言的编程中,允许使用函数的递归调用。所谓函数的递归调用是指在调用一个函数的过程中出现直接或间接调用该函数自身的情况。 递归调用的特点 在实际问题中,有许多问题可以采用递归调用的方法来解决。原有的问题能够分解为一个新的问题,而新的问题又用到了原有问题的解法,这就出现了递归。 使用递归调用方法编写的程序简洁清晰,可读性强,但时间和空间的开销大,不过对现代计算机影响不大。 递归调用的过程 “递推”阶段:将原问题不断分解为新的子问题,逐渐从未知向已知的方向推测,最终到达已知的条件,即递归结束条件。 “回归”阶段:从已知的条件出发,按照“递推”的逆过程,逐一求值回归,最后到达递推的开始处,结束回归阶段,完成递归调用。 东华大学信息科学与技术学院 龚涛

32 4.6 函数的嵌套调用和递归调用 4.6.2 函数的递归调用 例4.18 编程求出Fibonacci数列的第n项。
4.6 函数的嵌套调用和递归调用 函数的递归调用 例4.18 编程求出Fibonacci数列的第n项。 #include <iostream.h> const int N=8; long Fibo(int n); void main() { long f=Fibo(N); cout<<f<<endl; } long Fibo(int n) if(n==1) return 0; else if(n==2) return 1; else return Fibo(n-1)+Fibo(n-2); F(8)=F(7)+F(6) F(7)=F(6)+F(5) F(6)=F(5)+F(4) F(5)=F(4)+F(3) F(4)=F(3)+F(2) F(3)=F(2)+F(1) F(2)=1 F(2)=1 F(1)=0 东华大学信息科学与技术学院 龚涛

33 第4章 函数和作用域 4.7 作用域 4.7.1 标识符的作用域规则
第4章 函数和作用域 4.7 作用域 标识符的作用域规则 标识符的作用域规则如下:标识符只能在说明它或定义它的范围内是可见的,在该范围之外是不可见的。 对于大多数标识符,对它进行说明和定义是一回事。只有少数标识符例外,例如外部变量、函数和类等。 标识符包括了常量名、变量名、函数名、类名、对象名、语句标号等。凡是使用标识符规则定义的各种单词都属于标识符。 这里讲的范围有大有小,最大的是整个程序,最小的是块,中间的有文件和函数。标识符是在一定范围内定义的。 可见指的是可以进行存取或访问操作,不可见是指不可进行存取或访问擦作。 东华大学信息科学与技术学院 龚涛

34 4.7 作用域 4.7.2 作用域的种类 不同标识符定义在不同的范围内,有着不同的作用域。按作用域的大小可分为如下几类:
4.7 作用域 作用域的种类 不同标识符定义在不同的范围内,有着不同的作用域。按作用域的大小可分为如下几类: 程序级:程序级的作用域最大,它包含着组成该程序的所有文件。属于程序级作用域的有外部函数和外部变量。这类标识符是在某个文件中定义的,在该程序的其他文件中都是可见的,一般在访问之前需要加以说明。 文件级:属于文件级作用域的有内部函数和外部静态类变量,这种作用域仅在定义它的文件内。另外,用宏定义所定义的符号常量一般是属于文件级的。 函数级:属于函数级作用域的有函数的形参和在函数内定义的自动类变量、寄存器类变量和内部静态类变量以及语句标号。但不包括在该函数体内的分程序或者if语句、switch语句以及循环语句中所定义的变量。 块级:属于块级作用域的有定义在分程序、if语句、switch语句以及循环语句中的自动类变量、寄存器类变量和内部静态类变量。 东华大学信息科学与技术学院 龚涛

35 C++语言中,在相同的作用域内,不可有同名变量存在。但是,在不同的作用域内,允许对某个变量进行重新定义。
4.7 作用域 关于重新定义标识符的作用域规定 C++语言中,在相同的作用域内,不可有同名变量存在。但是,在不同的作用域内,允许对某个变量进行重新定义。 重新定义标识符的作用域规定如下: 在某个作用范围内定义的标识符在该范围内的子范围内中可以重新定义该标识符。这时原定义的标识符在子范围内是不可见的,但是它还是存在的,只是在子范围内由于出现了同名的标识符而被暂时隐藏起来。过了子范围后,它又是可见的。 东华大学信息科学与技术学院 龚涛

36 4.7 作用域 4.7.4 局部变量和全局变量 在C++程序中,所定义的变量可分为局部变量和全局变量两大类。 1. 局部变量
4.7 作用域 局部变量和全局变量 在C++程序中,所定义的变量可分为局部变量和全局变量两大类。 1. 局部变量 局部变量是指作用域在函数级和块级的变量,包括自动类变量、寄存器类变量和内部静态类变量以及函数参数。自动类变量是在函数体或分程序内定义的变量,它们的作用域分别在所定义的函数体或分程序内。 在定义寄存器类变量时,应注意如下几点: (1) 该变量的数据长度与通用寄存器的长度相当,一般是char型和int型变量。 (2) 寄存器类变量不宜定义过多,因为通用寄存器个数有限。 (3) 要选择一些使用频率高的变量优先定义为寄存器类变量。 内部静态类变量是定义在函数体内或分程序内,并且用说明符static说明的一种变量。它的作用域与自动变量相同,但是它的生存期(即寿命)较长。这是一种可见性与存在性不一致的变量。 东华大学信息科学与技术学院 龚涛

37 4.7 作用域 4.7.4 局部变量和全局变量 2. 全局变量 全局变量是指作用域在程序级和文件级的变量,包括外部变量和外部静态变量。
4.7 作用域 局部变量和全局变量 2. 全局变量 全局变量是指作用域在程序级和文件级的变量,包括外部变量和外部静态变量。 外部变量的作用域是程序级的,即在一个文件中定义的外部变量,在该程序的其他文件中都可使用。外部变量在引用之前需要说明,说明外部变量时应在前面加说明符extern表示该变量是外部变量。在一个程序中,一个外部变量只能定义一次,但是可以说明多次。 外部静态变量被定义在函数体外,定义时前边加说明符static表示为静态变量。外部静态变量的作用域是从定义该变量时起直到该文件结束,因此,称外部静态变量的作用域为文件级的。外部静态变量的可见性与存在性是不一致的,因为它的作用域不是整个程序,可它的寿命存在于整个程序。 东华大学信息科学与技术学院 龚涛

38 内部函数在定义它的文件中可以被调用,而在同一程序的其他文件中不可以被调用。定义内部函数的格式如下:
4.7 作用域 内部函数和外部函数 1. 内部函数 内部函数在定义它的文件中可以被调用,而在同一程序的其他文件中不可以被调用。定义内部函数的格式如下: static <类型说明> <函数名>(<参数表>) { <函数体> } 其中,static是关键字,用它说明的函数是静态函数,也称内部函数。 东华大学信息科学与技术学院 龚涛

39 4.7 作用域 4.7.5 内部函数和外部函数 例4.23 内部函数的定义和调用。 #include <iostream.h>
4.7 作用域 内部函数和外部函数 static int reset() { return i; } static int next(int j) j=i++; return j; static int last(int j) static int i(20); j=i--; static int other(int i) int j(15); return i=j+=i; 例4.23 内部函数的定义和调用。 #include <iostream.h> int i(10); static int reset(), next(int), last(int), other(int); void main() { int i=reset(); for(int j(1);j<=3;j++) cout<<i<<","<<j<<","; cout<<next(i)<<","; cout<<last(i)<<","; cout<<other(i+j)<<endl; } 东华大学信息科学与技术学院 龚涛

40 外部函数是一种作用域在整个程序中的函数,包括组成该程序的所有文件。外部函数的定义格式如下:
4.7 作用域 内部函数和外部函数 2. 外部函数 外部函数是一种作用域在整个程序中的函数,包括组成该程序的所有文件。外部函数的定义格式如下: [extern] <类型说明> <函数名>(<参数表>) { <函数体> } 其中,extern是关键字,它是外部类函数的说明符。一般情况下,它在定义函数时可以省略。 东华大学信息科学与技术学院 龚涛

41 4.7 作用域 4.7.5 内部函数和外部函数 例4.24 外部函数的定义和调用。 文件f1.cpp内容如下: 文件f2.cpp内容如下:
4.7 作用域 内部函数和外部函数 文件f2.cpp内容如下: static int i(10); extern int next() { return i+=1; } extern int last() return i-=1; extern int other(int i) static int j(15); return i=j+=1; 文件f3.cpp内容如下: extern int i; extern int reset() return i; 例4.24 外部函数的定义和调用。 文件f1.cpp内容如下: #include <iostream.h> int i(1); extern int reset(), next(), last(), other(int); void main() { int i=reset(); for(int j(1);j<=3;j++) cout<<i<<","<<j<<","; cout<<next()<<","; cout<<last()<<","; cout<<other(i+j)<<endl; } 东华大学信息科学与技术学院 龚涛

42 第4章 函数和作用域 4.8 C++语言的系统函数 4.8.1 C++语言系统函数概述
第4章 函数和作用域 4.8 C++语言的系统函数 C++语言系统函数概述 C++语言系统将所提供的系统函数的说明分类放在不同的.h文件(又称头文件)中。因此,编程者在使用系统函数时,应注意如下几点: 了解所使用的C++语言系统提供了哪些系统函数。 知道某个系统函数说明在哪个头文件中。 调用一个函数时,一定要将该函数的功能、参数和返回值搞清楚,否则难以正确使用这个函数。 东华大学信息科学与技术学院 龚涛

43 编译系统提供的字符串处理函数放在string.h头文件中,调用字符串处理函数时,要包含string.h文件。
4.8 C++语言的系统函数 字符串处理函数 编译系统提供的字符串处理函数放在string.h头文件中,调用字符串处理函数时,要包含string.h文件。 1. 字符串长度函数strlen() 该函数的功能是返回字符串的长度,该函数原型说明如下: int strlen(const char *s); 该函数将计算字符指针s所指向的字符串的长度,即字符串中包含的有效字符的个数,空字符不计算在内。 东华大学信息科学与技术学院 龚涛

44 该函数的功能是用一个字符串去更新另一字符串,进而实现字符串的复制。该函数原型说明如下:
4.8 C++语言的系统函数 字符串处理函数 2. 字符串复制函数strcpy() 该函数的功能是用一个字符串去更新另一字符串,进而实现字符串的复制。该函数原型说明如下: char * strcpy(char *s1, const char *s2); 其中,s1和s2是字符指针或者字符数组,而s2的字符串是已知的。该函数将s2所指向的字符串复制到s1所指向的字符数组中。然后返回s1的地址值。该函数是一个指针函数,因为它的返回值是一个指向char型的指针。必须保证s1所指向的对象能够容下s2所指向的字符串,否则将出现数据混乱。 东华大学信息科学与技术学院 龚涛

45 该函数的功能是将一个字符串添加到另一个字符串的后面,形成一个包含了两个字符串的新字符串。该字符串连接函数的原型说明如下:
4.8 C++语言的系统函数 字符串连接函数 3. 字符串连接函数strcat() 该函数的功能是将一个字符串添加到另一个字符串的后面,形成一个包含了两个字符串的新字符串。该字符串连接函数的原型说明如下: char * strcat(char *s1, char *s2); 其中,s1和s2都是字符指针或字符数组,该函数用来返回指针参数s1的值。调用该函数时,必须保证s1所指向的对象有足够空间能够容纳s2所添加的字符串。一般要求,s1所指的对象不小于strlen(s1)+ strlen(s2)+1。 东华大学信息科学与技术学院 龚涛

46 int * strcmp(const char *s1, const char *s2);
字符串处理函数 4. 字符串比较函数strcmp() 该函数的功能是用来比较两个字符串是否相等。如果该函数返回值为0,说明比较的两字符串相等。如果返回值为大于0的值,说明比较的两个字符串不相等,第一个大于第二个。如果返回值为小于0的值,则说明第一个小于第二个。该函数的原型说明如下: int * strcmp(const char *s1, const char *s2); int * strncmp(const char *s1, const char *s2, int n); 其中,s1和s2是字符指针或字符数组,n是整数值。 东华大学信息科学与技术学院 龚涛

47 第4章 函数和作用域 4.9 函数模板 函数模板的概念 函数模板是一种工具,它是C++语言支持参数化多态性的工具。模板是用来解决代码重用的一种方法。代码重用就是按不同方式重复使用代码,因此,要求重用代码要有通用性,即不受使用的数据类型的影响。这种程序设计类型称为参数化程序设计,而模板就是用来解决这一问题的。 模板实际上是对类型进行参数化,它是由可以使用和操作一定范围内的数据类型的通用代码构成的。 模板通常有两种不同形式:函数模板和类模板。 东华大学信息科学与技术学院 龚涛

48 4.9 函数模板 4.9.1 函数模板的概念 (1) 函数模板的引进
4.9 函数模板 函数模板的概念 (1) 函数模板的引进 重载函数是名字相同而函数体不同的多个函数,根据参数类型、个数和顺序不同进行选择。函数模板是通过对参数类型进行参数化后,获取具有相同形式的函数体。 函数模板是一个通用函数,它可适应于一定范围内的不同类型对象的操作。函数模板将代表着不同类型的一组函数,它们都使用相同的代码,这样可以实现代码重用,避免重复劳动,又可增强程序的安全性。这便是引进函数模板的目的。 东华大学信息科学与技术学院 龚涛

49 4.9 函数模板 4.9.1 函数模板的概念 (2) 函数模板的定义格式 函数模板的定义个数如下:
4.9 函数模板 函数模板的概念 (2) 函数模板的定义格式 函数模板的定义个数如下: template <<参数化类型名表>> <类型> <函数名>(<参数表>) { <函数体> } 其中,template是定义模板的关键字。<参数化类型名表>又称模板参数表,多个表项用逗号分隔。每个表项称为一个模板参数。 (3) 函数模板与模板函数 函数模板是模板函数的一个样板,它可以生成多个重载的模板函数,这些模板函数重用函数体代码。模板函数是函数模板的一个实例。 东华大学信息科学与技术学院 龚涛

50 答疑 例2.21 分析下列程序的输出结果 #include <iostream.h> void main() { union
例2.21 分析下列程序的输出结果 #include <iostream.h> void main() { union int a[3]; char s[12]; }u; u.a[0]=0x ; u.a[1]=0x474e494a; u.a[2]=0x00000a21; cout<<u.s; cout<<sizeof(u)<<endl; } a e 49 4a \0 ! G N I J I E B BEI JING! 12个字节 东华大学信息科学与技术学院 龚涛

51 答疑联系信息 办公室电话: 手机: QQ: 办公室地址:2号学院楼518室 东华大学信息科学与技术学院 龚涛


Download ppt "授课老师:龚涛 信息科学与技术学院 2017年3月 教材:《Visual C++程序员成长攻略》 《C++ Builder程序员成长攻略》"

Similar presentations


Ads by Google