C++语言程序设计课件Ch6-函数_第1页
C++语言程序设计课件Ch6-函数_第2页
C++语言程序设计课件Ch6-函数_第3页
C++语言程序设计课件Ch6-函数_第4页
C++语言程序设计课件Ch6-函数_第5页
已阅读5页,还剩105页未读 继续免费阅读

下载本文档

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

文档简介

内容提要函数的定义函数的调用函数的嵌套调用函数的递归调用局部变量和全局变量全局函数和静态函数

第6章

函数1内容提要内联函数函数重载默认参数的函数函数指针函数与数组、指针及引用等的关系命名空间

第6章

函数2Ch6函数

一个C++程序是由若干个源程序文件构成的,而一个源程序文件是由若干个函数构成。//Demo:源程序文件1.cpp+源程序文件2.cpp

3Ch6函数

从用户的角度看,有两种不同的函数:库函数和用户自定义函数。所谓库函数也称标准函数,由C++系统提供。而用户自定义函数则需要用户先定义,后使用。46.1函数的定义

6.1函数的定义6.1.1函数的定义格式定义函数的一般形式:函数返回值的数据类型标识符

函数名(形式参数表及其类型)

{函数体}56.1函数的定义

如:voiddisplay_larger(intx,inty){if(x<y)cout<<"Thelargeris:"<<y<<"\n";else

if(x>y)cout<<"Thelargeris:"<<x<<"\n";else

cout<<"Twovaluesareequal."<<"\n";}66.1函数的定义

在C++中定义函数时注意:(1)函数的形参及类型说明要采用新的ANSI标准,即必须放在函数名后面的括号内。(2)当形参有多个时,必须用逗号隔开。(3)如果函数是无参函数,括号也不能省略(括号内最好写上void)。(4)所有的函数都要先定义,后使用(调用)。(5)不能省略函数值的类型,必须表明该函数的函数值的类型,即使该函数没有返回值,也要注明函数值的类型为void。76.1函数的定义

6.1.2

函数的参数和函数的返回值所谓调用函数是指在程序中使用了该函数。

--主调函数(调用其他函数的函数)

--被调函数(被主调函数调用的函数)

--调用点(主调函数调用被调函数的地方)1.形式参数和实际参数(形参和实参)在调用函数时,大多数情况下,主调函数和被调函数之间有数据传递关系。而函数之间的数据传递就是靠函数的参数进行的,而对无参函数的调用,没有数据传递。86.1函数的定义

在定义函数时,函数名后面括号内的变量名为“形式参数”(形参)。在调用函数时,函数名后面括号内的表达式为“实际参数”(实参)。例

voidmain(void){

int

a,b,c;

cin>>x>>y;

c=max(a,b);

cout<<“maxis”<<c;

}

int

max(int

x,inty){

intz;z=x>y?x:y;return(z);

}96.1函数的定义

关于形参和实参说明几点:(1)实参可以是变量、常量、或表达式,但必须有确定的值。而形参必须是变量。(2)形参变量,只有存在发生函数调用时,形参才被分配存储单元,在调用结束时,形参所占的内存单元被释放。(3)实参与形参的类型必须一致,否则会发生“类型不匹配”的错误。(4)实参对形参的数据传递是“值传递”,即单向传递。由实参把数据传给形参,并且存储单元与形参是不同的单元,并将实参对应的值依次传递给形参变量。调用结束后,形参单元被释放,而实参单元保留并维持原值。106.1函数的定义

Demos:p163例--ch8_12.cpp值参数传递方式.cpp

参数传递方式.cpp

指针类型的值参数.cpp116.1函数的定义

2.函数的返回值(1)函数的返回值是通过函数中的return语句获得的,return语句的格式为:return(表达式);或return表达式;return语句的功能有两个,

(A)强制程序执行的流程从被调函数返回到主调函数;

(B)给主调函数带回一个确定的函数值。126.1函数的定义

如:

intmax(inta,intb){return(a>b?a:b);}136.1函数的定义

(2)函数值的类型:函数返回值的类型就是在定义函数时的函数值的类型。在定义函数时,函数值说明的类型和return语句中的表达式类型不一致时,则以函数类型为准。(3)如果被调用函数中没有return语句,为了明确表示函数“不返回值”,要用void定义无类型。如:voidprint(void){

cout<<“Clanguage\n”);}这样系统就保证不使函数带回任何值。//Demo:函数的定义和使用例.cpp

螺旋队列.cpp14内容提要函数的定义函数的调用函数的嵌套调用函数的递归调用局部变量和全局变量全局函数和静态函数

第6章

函数156.2函数的调用

6.2函数的调用1.函数调用的格式

函数名(实参数)

说明:(1)如果调用的是无参函数,则实参表可略去,但函数的括号不能省。

(2)如果实参表中有多个参数,之间用逗号隔开,实参的类型、个数应与形参顺序一一对应。

166.2函数的调用

函数通过下列三种方式完成函数调用:(1)函数调用语句:即以一个函数的调用后面加上“;”作为一个语句。如:fun(5);(2)函数表达式:即函数出现在一个表达式中,这时要求函数带回一个确定的值以参加表达式的运算。如:c=2*max(a,b);(3)函数参数:以函数的调用作为一个函数的实参。如:M=max(a,max(b,c));176.2函数的调用

2.调用函数时的前提条件在一个函数中调用另一个函数,需要具备的条件:(1)首先被调用的函数必须已经存在的函数。如果调用库函数,一般还应在本文件的开头用#include命令将调用有关库函数时所需用到的信息包含到本文件来。(2)如果调用用户自己定义的函数,则必须对被调函数的原型进行说明(否则必须函数先定义,后才能被调用),函数的原型包括:

函数值的类型标识符被调用函数名(形参及其类型表);

(3)对函数原型的说明,通常放在程序的顶部,也可以存放到一个头文件中,然后利用#include语句嵌入到程序中。//Demo:

mymain.cpp+myheaderfile.h186.2函数的调用

3.函数的定义与函数原型说明之间的区别(1)函数的“定义”是一个函数功能的确立,包括指定函数名,函数返回值的类型,形参及其类型,函数体等,它是一个完整的、独立的函数单位。(2)函数的原型说明则只是对将要使用的函数进行“框架”说明,它包括函数类型、函数名、一对括号及其括号内的各个函数形参的类型。形参名可以省略。(3)对函数进行原型说明的作用是告诉系统,在本程序中将要用到的函数是什么样的“结构模型”,以便确保在主调函数中必须按此“模型”对函数进行调用(即要匹配)。19内容提要函数的定义函数的调用函数的嵌套调用函数的递归调用局部变量和全局变量全局函数和静态函数

第6章

函数206.3函数的嵌套调用

6.3函数的嵌套调用

C++语言中函数的定义是平行的、独立的,所以,函数的定义是不能嵌套进行的,但函数的调用可以。嵌套调用即在调用一个函数的过程中,又调用另一函数。216.3函数的嵌套调用

main()函数{

……

调用a函数

……}

a()函数

{……

调用b函数

……}

b()函数

{…………}

226.3函数的嵌套调用

在本例中,main函数在执行过程中,调用了函数a,而函数a中又调用了函数b,所以,这就是一种嵌套调用。要注意:在函数嵌套调调用过程中程序执行的流程和返回点的问题。23内容提要函数的定义函数的调用函数的嵌套调用函数的递归调用局部变量和全局变量全局函数和静态函数

第6章

函数246.4函数的嵌套调用

6.4

函数的递归调用1.函数递归调用的概念函数的递归调用就是当一个函数在执行的过程中,出现了直接或间接地调用函数本身的函数调用方式。下面定义求n!的函数定义。longfact(longn){if(n==1)return1;returnfact(n-1)*n;//出现了函数fact自己直接调用本}//身的函数调用要正确地理解函数的递归调用时,程序执行的流程和返回点。256.4函数的嵌套调用

递归是用自己定义自己的过程,有时称为循环定义(circulardefinition)。递归的例子很多。例如,整数的一个递归定义是:0,1,2,3,4,5,6,7,8,9加或减一个整数是整数。因此,15是8加7,21是9加12,12是9加2,等等。266.4函数的嵌套调用

函数调用自己时,在栈上为局部变量和参量分配存储空间,从头执行函数代码。递归调用并不复制函数代码,只是新分配相应变量。每个递归调用返回时,其局部量和参数的存储空间都释放回栈中,执行在函数中的调用点继续。

许多函数的递归版本比等价的叠代版本运行得慢,原因在于反复调用得开销太大。实际上,过分递归可以溢出堆栈。递归函数的主要优点是能够生成某些算法的更清晰、更简洁的版本。如快速排序算法、汉诺塔问题等就很难用叠代方法实现。此外,某些问题(特别是人工智能有关的问题)本质上是递归的,特别适合递归解。最后,有些人认为递归比叠代更便于思考。276.4函数的嵌套调用

2.函数递归调用的条件

递归调用的条件也是我们在定义一个递归函数时应该遵循的原则。(1)必须有完成函数任务的语句。如:上例求n!中的return1;(2)有一个确定是否能避免递归调用的测试条件。如果条件不满足时就递归调用,否则就不再递归调用。(3)有一个递归调用语句,并且该递归调用语句的参数应该逐渐逼近不满足条件,以致最后断绝递归。(4)先测试,后递归调用。在递归函数定义中,必须先测试,后递归调用。也就是说,递归是有条件的,满足了条件后,才可以递归。286.4函数的嵌套调用

Demos:递归函数例7.cpp猴子吃桃问题的递归解决.cpp递归函数例3.cpp递归函数例4.cpp,递归函数例1.cpp递归函数例2.cpp,递归函数例6.C递归函数例-整数的逆向输出.cpp递归函数实例.cpp

牛数的递归算法2.cpp,牛数的递归算法1.cpp汉诺塔问题.cpp,递归函数例5.cpp,递归函数例子.cpp走台阶问题.cpp

最大公约数的递归解法.cpp

递归求满足不等式的n值.cpp

十进制转化为二进制递归解法.cpp29内容提要函数的定义函数的调用函数的嵌套调用函数的递归调用局部变量和全局变量全局函数和静态函数

第6章

函数306.5局部变量和全局变量

6.5局部变量和全局变量从程序中各个变量起作用的范围(作用域)来看,变量可以分为局部变量和全局变量。1.局部变量在一个函数的内部定义的变量就是内部变量(局部变量)。如:

floatf1(inta){

int

b,c;//a,b,c有效

……}voidmain(void){

int

n,m;//m,n有效

……}31对局部变量作以下几点说明:(1)局部变量的作用范围(作用域):只局限在定义它的本函数体之内。(2)局部变量的生存期(存在的时间):只有当程序执行到本函数时,才给这些局部变量分配存储单元,当本函数执行完毕后,这些局部变量所占存储单元就被释放。(3)不同函数体中可以定义相同名字的变量,但它们代表不同的对象,互不干扰。它们在内存占用不同的内存单元。(4)函数的形式参数也是该函数的局部变量,其他函数不能调用。(5)在一个函数内部,可以在复合语句中定义变量,但这些变量只在本复合语句中有效,因此,复合语句也可称为“分程序”或“程序块”。6.5局部变量和全局变量32(6)局部变量又可称自动变量(auto),(因为可以用关键字auto说明之)。使用auto存储类型定义的变量称为自动变量,编译系统在堆栈区(内存的一部分)为它分配内存空间。分配和释放空间的操作都由系统自动完成,故称为自动变量。函数内定义的自动变量关键字auto可以缺省。6.5局部变量和全局变量332.静态局部变量(局部静态变量)

定义方法是:在函数体内定义变量采用:

static类型标识符变量名;特点:

(1)静态局部变量本身也是局部变量,因此其作用域也是局限在定义它的本函数体内,当离开本函数体,该变量就不再起作用,但其值还继续保留。(2)另一方面,静态局部变量又是静态存储类别的变量,所以,在整个程序运行开始就被分配固定的存储单元(占用静态存储区),整个程序运行期间不再被重新分配,所以其生存期是整个程序运行期间。(3)静态局部变量的赋初值的时间在编译阶段,并不是每发生一次函数调用就赋一次初值。当再次调用该函数时,静态局部变量保留上次调用函数时的值。//Demo:static.cpp

6.5局部变量和全局变量343.全局变量

在所有函数体外部定义的变量为外部变量(全局变量),全局变量可以被本文件中其他函数所调用(使用)。全局变量的有效范围为:从定义该变量的位置开始到本程序文件的结束。如:6.5局部变量和全局变量35intp=1,q=5;//全局变量floatf1(inta){

int

b,c;……//局部变量}charc1,c2; //全局变量charf2(intx,inty){

int

i,j;……}voidmain(void){

int

m,n;……}6.5局部变量和全局变量36对全局变量的几点说明:(1)全局变量在程序的全部执行过程中都占用固定的内存储单元,而不是仅在需要时才开辟单元,所以其生存期是整个程序运行期间。6.5局部变量和全局变量(2)全局变量的作用范围是整个程序文件。全局变量处于文件的开头定义时,其作用范围为整个文件,否则在定义点之前的函数内使用时,应在函数体内或外部用extern说明。

37说明的格式是:

extern类型标识符变量名;//Demo:aboutglobal1.cppaboutglobal2.cpp6.5局部变量和全局变量38(3)在一个程序文件中定义的全局变量,要在同一程序的另外一个程序文件中使用时,应在使用它的程序文件中所有函数体内部或外部对所使用的全局变量用extern说明(外部变量)。

//Demo:externvariation.cpp+myfun.h+myfun1.cpp+myfun2.cpp(4)在同一个文件中全局变量与局部变量同名时,则在局部变量的作用范围内,全局变量不起作用。(可以使用域作用符::访问全局变量)//Demo:域作用符.cpp6.5局部变量和全局变量394.静态全局变量

当在所有函数体的外部用如下格式定义变量时,则这种变量称为静态全局变量。

static类型标识符变量名;静态全局变量的特点是:(1)与全局变量基本相同,只是其作用范围(即作用域)是定义它的程序文件,而不是整个程序。(2)静态全局变量属于静态存储类别的变量,所以它在程序一开始运行时,就被分配固定的存储单元,所以其生存期是整个程序运行期间。(3)使用静态全局变量的好处是同一程序的两个不同的程序文件中可以使用相同名称的变量名,而互不干扰。6.5局部变量和全局变量40注:变量的类型还有寄存器类型(register)//Demo:aboutregister.cpp6.5局部变量和全局变量41小结:从变量作用域和生存期的角度出发,把覆盖了从定义点开始到整个程序结束的变量称为具有全局作用域和全局寿命的变量,简称为“全局变量”(GlobalVariable),而把作用域和生存期只覆盖了某个函数或某个复合语句的变量称为具有局部作用域和局部寿命的变量,简称为“局部变量”(LocalVariable),它包含如下存储类型的变量:自动变量、静态局部变量、寄存器变量和形参(形参也是自动型,也可说明为寄存器变量)。(注意局部静态变量的生存期是整个程序)在所有函数外定义的变量称为外部变量,若将它的作用域限制在一个源文件内,需加上关键字static,称为静态全局变量。这两种变量统称为全局变量。6.5局部变量和全局变量42内容提要函数的定义函数的调用函数的嵌套调用函数的递归调用局部变量和全局变量全局函数和静态函数

第6章

函数436.6全局函数和静态函数1.全局函数(类似全局变量)

凡程序中定义的函数,如果未做特别说明,一律都是全局的。也就是说,函数从本质上是全局的,一个程序不管有多少个程序文件所组成,在某个程序文件中可以调用同一程序的另外一个程序文件中定义的函数。(可以缺省extern关键字),只要对被调函数的原型进行说明即可。6.6全局函数和静态函数44声明函数原型的语句:

类型标识符函数名(形参类型表);如:假设程序中定义了max函数,则:int

max(int,int);就是对函数max原型的声明。//Demos:函数原型.cpp

externvariation.cpp+myfun.h+myfun1.cpp+myfun2.cpp6.6全局函数和静态函数452.静态函数因为函数多数为外部型的,必然产生不同源文件中同名函数的冲突。若将某一重要函数定义为静态型(static),则只能被该函数所在文件的其它函数调用,而不能被其它源文件的函数调用。静态函数定义方法为:static类型标识符函数名(形参及其类型){

函数体}注意:这种函数就只能在定义它的程序文件中调用。6.6全局函数和静态函数46内容提要内联函数函数重载默认参数的函数函数指针函数与数组、指针及引用等的关系命名空间

第6章

函数476.7内联函数1.内联函数的定义方法和格式:

inline

函数值的类型函数名(形参及其类型列表){

函数体

}6.7内联函数48#include<iostream>usingnamespacestd;inlinedoublesquare(doublex){returnx*x;}voidmain(void){doublex;

cout<<”inputadata”;cin>>x;

cout<<”thesqureis“<<square(x);}6.7内联函数49

2.内联函数与普通函数的区别和联系(1)在定义内联函数时,函数值的类型左面有“inline”关键字,而普通函数在定义时没有此关键字。(2)程序中调用内联函数与调用普通函数的方法相同。(3)当在程序中调用一个内联函数时,是将该函数的代码直接插入到调用点,然后执行该段代码,所以在调用过程中不存在程序流程的跳转和返回问题。而普通函数的调用,程序是从主调函数的调用点转去执行被调函数,待被调函数执行完毕后,再返回到主调函数的调用点的下一语句继续执行。6.7内联函数50

(4)从调用机理看,内联函数可加快程序代码的执行速度和效率,但这是以增加程序代码为代价来求得速度的。6.7内联函数513.对内联函数的限制应注意:不是任何一个函数都可定义成内联函数。(1)内联函数的函数体内不能含有复杂的结构控制语句,如:switch和while,如果内联函数的函数体内有这些语句,则编译将该函数视同普通函数那样产生函数调用代码。(2)递归函数不能被用来作为内联函数。(3)内联函数一般适合于只有1~5行语句的小函数,对一个含有很多语句的大函数,没有必要使用内联函数来实现。6.7内联函数52

6.8函数重载1.什么是函数重载函数重载是指一个函数可以和同一作用域中的其他函数具有相同的名字,但这些同名函数的参数类型、参数个数、返回值、函数功能可以完全不同。如:6.8函数重载53

#include<iostream>usingnamespacestd;voidwhatitis(inti){

cout<<"thisisinteger"<<i<<endl;}voidwhatitis(charc[]){

cout<<“thisisstring”<<c<<endl;}voidmain(void){

inti=1;charc[]="abcdef";

whatitis(i);

whatitis(c);}6.8函数重载54内容提要内联函数函数重载默认参数的函数函数指针函数与数组、指针及引用等的关系命名空间

第6章

函数55在本例中定义了两个名称都叫whatitis的函数,但它们的形参类型不同。因此,这两个函数就是重载函数。2.为什么要使用函数重载在原有C语言中,每个函数必须有其唯一的名称,这样的缺点是所有具有相同功能、而只是函数参数不一样的函数,就必须用一个不同的名称,而C++中采用了函数重载后,对于具有同一功能的函数,如果只是由于函数参数类型不一样,则可以定义相同名称的函数。6.8函数重载563.匹配重载函数的顺序由于重载函数具有相同的函数名,在进行函数调用时,系统一般按照调用函数时的参数个数、类型和顺序来确定被调用的函数。具体来说,按以下三个步骤的先后次序找到并调用那个函数:(1)寻找一个严格的匹配,即:调用与实参的数据类型、个数完全相同的那个函数。(2)通过内部转换寻求一个匹配,即:通过(1)的方法没有找到相匹配的函数时,则由C++系统对实参的数据类型进行内部转换,转换完毕后,如果有匹配的函数存在,则执行该函数。(3)通过用户定义的转换寻求一个匹配,若能查出有唯一的一组转换,就调用那个函数。即:在函数调用处由程序员对实参进行强制类型转换,以此作为查找相匹配的函数的依据。6.8函数重载57#include<iostream>usingnamespacestd;voidprint(doubled){

cout<<"thisisadouble"<<d<<"\n";}voidprint(inti){

cout<<"thisisaninteger"<<i<<"\n";}voidmain(void){

intx=1,z=10;floaty=1.0;charc='a';

print(x); //按规则(1)自动匹配函数voidprint(inti)

print(y); //按规则(2)通过内部转换匹配函数

//voidprint(doublei)6.8函数重载58//因为系统能自动将float型转换成double型

print(c); //按规则(2)通过内部转换匹配函数

//voidprint(inti)//因为系统能自动将char型转换成int型

print(double(z));//按规则(3)匹配//voidprint(doublei)因为程序中将实参z强制转换//为double型。}6.8函数重载594.定义重载函数时的注意事项(1)重载函数间不能只是函数的返回值不同,应至少在形参的个数、参数类型或参数顺序上有所不同。如:6.8函数重载voidmyfun(inti){………………}int

myfun(inti){………………}这种重载就是错误的。(2)应使所有的重载函数的功能相同。如果让重载函数完成不同的功能,会破坏程序的可读性。60内容提要内联函数函数重载默认参数的函数函数指针函数与数组、指针及引用等的关系命名空间

第6章

函数616.9默认参数的函数1.默认参数的函数C++允许在定义函数时给其中的某个或某些形式参数指定默认值,这样,当发生函数调用时,如果省略了对应位置上的实参的值时,则在执行被调函数时,以该形参的默认值进行运算。如:6.9默认参数的重载62#include<iostream>usingnamespacestd;voidsum(intnum=10){//形参默认值

int

i,s=0;

for(i=1;i<=num;i++)s=s+i;

cout<<"sumis"<<s<<"\n";}voidmain(void){sum(100);//提供了实参值,被调函数以100进行运算,//输出结果为5050sum();//省略实参值,使用形参默认值10,输出结}//果为556.9默认参数的重载632.使用默认参数的函数的注意事项(1)默认参数一般在函数说明中提供。如果程序中既有函数的说明又有函数的定义时,则定义函数时不允许再定义参数的默认值。如果程序中只有函数的定义,而没有说明函数,则默认参数才可出现在函数定义中。如:voidpoint(intx=10,y=20);voidmain(void){…………}voidpoint(intx,inty){

cout<<x<<endl;

cout<<y<<endl;

}6.9默认参数的重载64(2)默认参数的顺序:如果一个函数中有多个默认参数时,则形参分布中,默认参数应从右至左连续定义,即指定和不指定不能交叉。如:voidmyfunc(inta=1,floatb,longc=20); //错误voidmyfunc(int

a,floatb=2.1,longc=30); //正确(3)当进行调用时,实参数目少于形参时,编译系统按从右到左的顺序用默认值来补足所缺少的实参。(从左到右使用实参,右边剩余的使用默认参数)//Demo:函数的默认参数.cpp6.9默认参数的函数65内容提要内联函数函数重载默认参数的函数函数与指针函数与数组及引用等的关系命名空间

第6章

函数66函数与指针的关系非常紧密,在学习函数之后,我们应该理解和掌握函数与指针的各种关系。1.指针作为函数的参数2.函数返回指针类型3.使用指向函数的指针6.10函数与指针671.指针作为函数的参数#include<iostream>usingnamespacestd;voidfun(int*pi){ (*pi)++;}voidmain(void){

inti=100;

int*p=&i;

fun(p);

cout<<"i="<<i<<endl;}思考:程序的输出?6.10函数与指针68#include<iostream>usingnamespacestd;voidfun(int*pi){ (*pi)++;}voidmain(void){

inta[]={1,2,3,4,5};

int*p=a+3;

fun(p);

for(inti=0;i<sizeof(a)/sizeof(int);i++)cout<<a[i]<<""; }思考:程序的输出?6.10函数与指针69#include<iostream>usingnamespacestd;voidfun(int*p){ p++;}voidmain(void){

inta[]={1,2,3,4,5};

int*q=a;

cout<<"*q="<<*q<<endl;

fun(q);

cout<<"*q="<<*q<<endl; }思考:程序的输出?如何使p的变化带回给q?6.10函数与指针70#include<iostream>usingnamespacestd;voidfun(int*&p){//对指针类型变量的引用,作为参数 p++;}voidmain(void){

inta[]={1,2,3,4,5};

int*q=a;

cout<<"*q="<<*q<<endl;

fun(q); cout<<"*q="<<*q<<endl; }6.10函数与指针712.函数的返回值类型为指针类型#include<iostream>usingnamespacestd;int*fun(int

a[],intn){ staticintmax=a[0];//如果去掉static,能编译通过吗?

for(intk=1;k<n;k++)if(a[k]>max)max=a[k]; return&max;}voidmain(void){

inta[]={1,2,3,14,5};

int*pmax;

pmax=fun(a,sizeof(a)/sizeof(int));

cout<<"*pmax="<<*pmax<<endl; }6.10函数与指针72注意:函数返回指针时,不能返回局部变量的指针。可以返回静态变量的指针,全局变量的指针以及动态存储分配的地址。如:#include<iostream>usingnamespacestd;int*fun(void){

int*p=newint; returnp;}voidmain(void){

int*q=fun(); *q=100;

cout<<"*q="<<*q<<endl;}6.10函数与指针732.指向函数的指针可以定义一个指向函数的指针。定义格式为:函数返回值类型(*指向函数的指针名)(参数列表)如:int(*pfun)(int,int);声明了一个指向返回值类型为int,带两个int参数的指针pfun。该如何使用pfun呢?6.10函数与指针74使用pfun的方法同其它指针变量。注意:使用指针变量前,一定要确定该指针变量有所指!通过pfun可以调用它所指向的函数。使用方法如下:

pfun(实参);如:int

sum(int

x,inty){returnx+y;}

int(*pfun)(int,int);//声明pfun

pfun=sum;//pfun有所指

cout<<pfun(12,40)<<endl;//使用pfun6.10函数与指针75#include<iostream>usingnamespacestd;int

sum(int

a,intb){returna+b;}voidmain(void){

int(*pfun)(int,int);

pfun=sum;//初始化指针变量pfun

intc=pfun(10,20);

cout<<"c="<<c<<endl; }6.10函数与指针76指向函数的指针变量可以作为某一个函数的参数。这样,当传递给该指针参数(指向函数)不同的实参值时,就可以通过使用该指向函数的指针参数,而达到调用不同函数的目的。如:6.10函数与指针77#include<iostream>usingnamespacestd;int

sum(int

a,intb){returna+b;}int

max(int

x,inty){ returnx>y?x:y;}int

fun(int

xx,int

yy,int(*pfun)(int

xx,int

yy)){

inttemp; temp=pfun(xx,yy); returntemp;}voidmain(void){

int(*p)(int,int);p=sum;cout<<fun(10,20,p)<<endl;//调用sum函数 p=max;cout<<fun(10,20,p)<<endl; //调用max函数 }6.10函数与指针78类似地,可以使用指向函数的指针数组。#include<iostream>usingnamespacestd;typedef

int(*PFunType)(int,int);//定义指向函数的指针类型PFunTypeint

sum(int

a,intb){returna+b;}int

max(int

x,inty){ returnx>y?x:y;}int

fun(int

xx,int

yy,int(*pfun)(intxx,int

yy)){

inttemp=pfun(xx,yy); returntemp;}voidmain(void){

PFunTypep[]={sum,max};//函数指针数组初始化

//等价于int(*p[2])(int,int)={sum,max};

cout<<fun(10,20,p[0])<<endl;//使用指针数组元素p[0]调用sum

cout<<fun(10,20,p[1])<<endl;//使用指针数组元素p[1]调用max}6.10函数与指针79main()的变元argc和argv(命令行参数)//已经在指针与引用一章讲述

有时,需要向程序传入信息。我们一般

通过命令行向主函数main()传递信息。命令行变元(commandlineargument)是操作系统命令行中,执行程序名字之后的信息。例如,在UNIX系统下编译C程序时,在提示符下键入类似下行的内容。

cc

program_name

其中,program_name是准备被编译的程序。程序名字program_name作为参数传给命令cc(编译程序)。6.10函数与指针80类似地,C++的源程序经编译、链接后生成了可执行的程序(如:myprogram.exe)后,允许向该程序传送参数,形式为D:\ms\>myprogrampara1para2…paraN

那么,该如何给C++程序传递参数呢?6.10函数与指针81

main(int

argc,char*argv[]))函数中的两个参数用于接受运行程序时命令行传入的参数信息,一个是argc,另一个是argv。

argc是整型变量,用于存放命令行中参数的总数(因为程序名也计算在内,argc的值最小为1)。形参argv是一个指针数组,指针数组中的每个元素(是一个指针)都分别指向相应的一个命令行变元(字符串)。6.10函数与指针82如:D:\>myprogrampara1para2para3则argc值为4,指针argv[0]指向命令行参数myprogram(即程序名),指针argv[1]指向命令行参数para1,指针argv[2]指向命令行参数para2,指针argv[3]指向命令行参数para3。

//Demos:

aboutmain.cpp程序设计其他问题面试题.cpp6.10函数与指针83内容提要内联函数函数重载默认参数的函数函数与指针函数与数组及引用等的关系命名空间

第6章

函数84函数与数组及引用等的关系,请参考上章“指针与引用”中的相关介绍。这里不再重叙。6.11命名空间85内容提要内联函数函数重载默认参数的函数函数与指针函数与数组及引用等的关系命名空间

第6章

函数86本节讨论在C++标准中介绍的命名空间的基本概念。命名空间提供了一种在程序内和程序之间组织和使用名称的技术。1.背景给人和物命名可能是困难的。在家庭内部可以说出不同的家庭成员的名称,比如John、George、Ann和Alice。在家庭内,当说到Ann的时候,每个人都知道谈论的是谁。然而在家庭之外,有许多名叫Ann的人。当需要在家庭外部谈论她的时候,可能需要限定她的名称,需要说明谈论的是哪一个Ann,否则有人可能想到另一个名为Ann的人。6.11命名空间87为了限制谈论的是哪一个Ann,一种方法是将她与家庭名称相联系。例如,在Washingtons家庭内部,每个人都用Ann的名字称呼她;在Nguyens家庭内部,每个人也都用Ann的名字称呼这个家庭的Ann。但是在家庭外部,她们被称为”Washingtons,Ann”和”Nguyens,Ann”。分开姓和名的逗号称为家庭分辨操作符。在编程的过程中,特别对于大型程序而言,也有相同的问题。我们需要命名程序内的常数、变量、函数和其他对象。但是当我们自己、其他项目组成成员或者系统程序员将我们的程序与其他程序结合在一起时,将出现什么情况?有些名称可能用在不止一个函数或者对象中。现在遇到了麻烦,如果两个程序段使用相同的名称—比如num—来定义一个整数,那么当引用num时,究竟指的是哪一个num是不清楚的。6.11命名空间88为了解决这个问题,C++定义了命名空间。命名空间是属于相同组的名称的集合。例如,可以有两个组:G1和G2。如果在每个组内定义一个名为num的变量,那么就可以没有任何歧义地在G1内部或者在G2内部引用num。但是如果需要在组外引用num,则必须用作用域分辨符(::)来限定名称。换句话说,G1内的num叫G1::num,而G2内的num叫G2::num。下图显示出C++如何使用命名空间来定义命名空间。namespaceG1namespaceG2{{intnum;intnum;… …}//namespaceG1}//namespaceG2G1::num=5;G2::num=10;6.11命名空间89为了使这个概念清晰,现在编写一个非常不寻常的程序。虽然永远不建议这样做,但是这里还是编写了一个两次使用名称cout的程序。第一个cout是一个变量名(值为5)的名称,因为它没有使用作用域分辨操作符,所以它的作用域局限于程序中的main。第二个cout使用了作用域分辨操作符,它将cout名称与std命名空间相联系,该命名空间定义所有在c++程序库中使用的名称。当使用cout时,编译器知道它应该检查程序内的声明,当使用std::cout时,该程序在我们的程序内找不到这个全称(姓和名),因此它尝试在头文件内寻找,并且在头文件<iostream>内发现了这个名称。以下程序包含这个程序的代码和结果。6.11命名空间90/*Demonstratenamespacescope.*/#include<iostream>intmain(){

int

cout=5;

std::cout<<cout<<“\n”;return0;}//mainResult:5程序证明:cout和std::cout是完全不同的两个名称。一个属于全局命名空间,另一个属于std命名空间。6.11命名空间91为了进一步研究,现在编写一个仅使用cout作为输出流名称的程序。//Demonstratenotinscopeerror.#include<iostream>intmain(){

cout<<“Hello\n”;return0;}//mainResults:Error:undefinedidentifier‘cout0’这个程序不能编译,因为编译器要寻找cout的声明,但是却没有发现。它既没有在程序内,也没有在iostream文件内发现cout的声明。iostream文件使用的是std::cout,而不是cout。6.11命名空间922.使用命名空间的名称为了使用特定命名空间的名称,有3种选择:写出全称、添加使用命令或者添加使用声明。(1)全称可能解决这个问题最简单的方法就是使用全称。尽管这样更简单,但是它需要更多的击键,而且程序更长。当使用全称时要将该命名空间的家族名称(比如std)和作用域分辨操作符添加到名称上。因此,cout变成std::cout。如:#include<iosteam>voidmain(){

std:cout<<“Enteraninteger:”;

int

numIn;

std:cin>>numIn;

std::cout<<“Youentered:“<<numIn<<“\n”;}6.11命名空间93(2)使用命令第2种解决方法是添加名为使用命令的语句,通常将其放在包含语句后面的程序全局区域。语句格式如下所示。它必须以分号结束。usingnamespacename_of_namespace;当编译器遇到未声明的名称时,它就将using命令定义的命名空间的名称添加到未声明的名称上,然后试图发现这个全称的生命。我们这次使用命名空间命令来命令编译器,为所有找不到的名称使用std命名空间。6.11命名空间94#include<iostream>usingnamespacestd;voidmain(){

cout<<“Enteraninteger:”;

int

numIn;

cin>>numIn;

cout<<“Youentered:“<<numIn<<“\n”;}//main6.11命名空间95(3)使用声明使用命令的方法将命名空间的名称添加到每一个编译器找不到的名称。第三种解决方法—使用声明,对编译器试图解析的名称进行了限制。它告诉编译器只将命名空间添加到选定数量的名称上,而不是全部找不到的名称上。其格式如下:usingname_of_the_namespace::identifier;当编译器遇到的名称包含在using声明内声明的标识符时,它就添加命名空间的名称,并再次尝试。例如,下面程序增加了两条使用命名空间的声明,来命令编译器只将std添加到cin、cout和endl;6.11命名空间96#include<iostream>usingstd::cout;usingstd::cin;usingstd::endl;voidmain(){

cout<<“Enteraninteger:”;

int

numIn;

cin>>numIn;

cout<<“Youentered:“<<numIn<<“\n”;}//main6.11命名空间97(4)命令和声明之间的区别使用声明将它的名称添加到局部作用域,使用命令使名称在声明她们的作用域内(通常是编译单元)可以使用。命名空间的命令和声明之间的另一个主要区别是,using语句包括的名称数量不同。命令添加所有命名空间内的名称,而声明只添加指定的名称。通常使用声明更安全,尤其是在多于一个名称空间时。看一个例子,假设有两个命名空间:purchasing和inventory。她们在程序内声明,都有两个普通的名称:i和j。这里想使用来自purchasing的i和来自inventory的j。如果在这种情况下使用命令,编译器将产生错误。但是,声明就可以限制能够使用的变量,并且不会出现错误。命令编译器每当发现i时就使用purchasing::i,而每当发现j时就使用inventory::j。6.11命名空间98#include<iostream>usingnamespacestd;namespacepurchasing{

inti;intj;}//purchasingnamespacenamespaceinventory{

inti;intj;}//inventorynamespaceusingpurchasing::i;usinginventory::j;//usingnamespaceinventory;voidmain(){i=5;cout<<“Purchasingiis:”<<purchasing::i<<endl;j=12;cout<<“Inventoryjis:”<<inventory::j<<endl;

cout<<i<<j<<std::endl;}//main6.11命名空间994.声明命名空间前面已经非正式地说明了如何声明命名空间

温馨提示

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

评论

0/150

提交评论