




版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领
文档简介
第2章函数的定义当设计较大程序时,通常需要用若干个模块来实现较复杂的功能,而每一个模块自成结构,用来解决一些子问题。这种能完成某一独立功能的子程序模块,在C++中成为函数。可见,函数是实现模块化程序的基本单位。事实上,函数还能体现代码重用的思想,因为一个函数可以在同一个程序中被多次调用或在多个程序中被调用。第2章函数和作用域2.1函数定义和调用2.2C++函数特性2.3作用域和存储类型2.4名称空间2.1函数定义和调用2.1.1函数定义C++的任何一个程序都可由一个主函数和若干个子函数组合而成。主函数可以调用子函数,子函数还可以调用其他子函数。C++规定主函数名必须是main,而其他函数可以是库函数或自定义函数。(1)主函数main不仅是程序的入口函数,而且与其他函数相比较还有许多使用上的限制。例如,它不能被其他函数调用,不能用inline和static来说明等。(2)库函数,又称标准函数,是ANSI/ISOC++编译系统已经预先定义好的函数,程序设计时可根据实际需要,直接使用这类函数,而不必重新定义。调用时,必须在程序中包含相应的头文件,并指明使用名称空间std。2.1函数定义和调用
(3)自定义函数是用户根据程序的需要,将某一个功能相对独立的程序定义成的一个函数,或将解决某个问题的算法用一个函数来组织。
与变量的使用规则相同,在C++程序中一定要先说明和定义函数,然后才能调用函数。C++中每一个函数的定义都是由4个部分组成的,即函数名、函数类型、形式参数表和函数体,其定义的格式如下:
<函数类型><函数名>(<形式参数表>){ <若干语句>}函数体2.1.1函数定义其中,函数名应是一个合法有效的C++标识符;函数头的形式参数又简称为形参。参数表中的每一个形参都是由形参的数据类型和形参名来构成,根据上述定义格式,可以编写一个函数sum,如图2.1所示,注意它们的书写规范。intsum(intx,inty){intz=x+y;returnz;}对齐函数头函数体缩进函数类型函数名形参图2.1定义一个函数sum2.1.1函数定义需要说明的是:(1)C/C++不允许在一个函数体中再定义函数,即禁止嵌套定义,但允许嵌套调用。(2)函数体也可不含有任何语句,这样的函数称为空函数,它仅为程序结构而设定,本身没有任何操作。(3)函数类型决定了函数所需要的返回值类型,它可以是除数组类型之外的任何有效的C++数据类型,包括引用、指针等。2.1.1函数定义(4)若函数类型为void时,则表示该函数没有返回值。但仍然可以在函数体中使用return语句“return
;”,此时可将“return;”语句理解为是函数体花括号“}”的作用,当流程遇到函数体的“}”时,函数调用结束,控制权返回给主调函数。例如: voidf1(inta) { if(a>10)
return; //return;一旦执行,后面的语句不再被执行
… }当a>10条件满足时,“return;”语句将控制权返回给主调函数。2.1.2函数的调用和声明1.函数的实参和形参定义一个函数就是为了以后的调用。调用函数时,先写函数名,然后紧跟括号,括号里是实际调用该函数时所给定的参数,称为实际参数,简称实参,并与形参相对应。要注意形参和实参的区别:(1)从模块概念来说,形参是函数的接口,是存在于函数内部的变量。而实参是存在于函数外部的变量。它们不是同一个实体,也就是说,形参变量和实参变量所对应的内存空间不是同一个内存空间。(2)按函数定义时所指定的形参类型,实参除变量外还可以是数值或表达式等,而形参只能是变量。(3)形参在函数调用之前是不存在的,只有在发生函数调用时,函数中的形参才会被分配内存空间,然后执行函数体中的语句,而当调用结束后,形参所占的内存空间又会被释放。2.1.2函数的调用和声明2.函数的调用函数调用的一般格式为:
<函数名>(<实参表>);调用函数时要注意:实参与形参的个数应相等,类型应一致,且按顺序对应,一一传递数据。
例如,下面的示例用来输出一个三角形的图案。2.1.2函数的调用和声明
[例Ex_Call]函数的调用#include<iostream>usingnamespacestd;voidprintline(charch,intn){ for(inti=0;i<n;i++) cout<<ch; cout<<endl; }intmain(){ introw=5; for(inti=0;i<row;i++)
printline('*',i+1); //A return0; }
2.1.2函数的调用和声明程序运行的结果如下:代码中,main函数的for循环语句共调用了5次printline函数(A句),每次调用时因实参i+1值不断改变,从而使函数printline打印出来的星号个数也随之改变。
2.1.2函数的调用和声明3.函数的声明由于前面函数printline的定义代码是放在main函数中调用语句A之前,因而A语句执行不会有问题。但若将函数printline的定义代码放在调用语句A之后,即函数定义在后,而调用在前,就会产生“printline标识符未定义”的编译错误。此时必须在调用前进行函数声明。2.1.2函数的声明声明一个函数按下列格式进行:
<函数类型><函数名>(<形参表>);可见,函数声明的格式是在函数头的后面加上分号“;”。但要注意,函数声明的内容应和函数的定义应相同。例如,对于前面sum函数和最后一个printline函数可有如下声明: intsum(intx,inty); voidprintline(charch,intn);2.1.3值传递函数的调用实质上就是参数传递,在C++中,函数的参数传递有两种方式,一是按值传递,二是地址传递或引用传递。这里先来说明按值传递的参数传递方法,地址传递或引用传递在以后来讨论。当函数的形参定义成一般变量时,如前面printline和sum函数的形参都是一般变量,此时函数的参数传递就是按值传递方式,简称值传递,是指当一个函数被调用时,C++根据实参和形参的对应关系将实际参数的值一一传递给形参,供函数执行时使用。2.1.3值传递值传递的特点是:(1)若实参指定是一般变量,则传递的是实参变量的值而不是实参变量的地址。(2)在执行函数代码时,由于对实参数据的操作最终是在形参的内存空间中进行,因此形参值的改变只是改变了形参的内存空间存储的值,而不会改变实参变量所对应的内存空间的值。也就是说,即使形参的值在函数中发生了变化,函数调用结束后,实参的值不会受到影响。例如:2.1.3值传递[例Ex_SwapValue]交换函数两个参数的值。#include<iostream>usingnamespacestd;voidswap(floatx,floaty);
//函数原型说明intmain(){ floata=20,b=40; cout<<"a="<<a<<",b="<<b<<"\n";
swap(a,b);
//函数调用 cout<<"a="<<a<<",b="<<b<<"\n"; return0;}voidswap(floatx,floaty)
//函数定义{ floattemp; temp=x;x=y;y=temp; cout<<"x="<<x<<",y="<<y<<"\n";}2.1.3值传递程序的运行结果为:可以看出,虽然函数swap中交换了两个形参x和y的值,但交换的结果并不能改变实参的值,所以调用该函数后,变量a和b的值仍然为原来的值。2.1.4函数的默认形参值在C++中,允许在函数的声明或定义时给一个或多个参数指定默认值。这样在调用时,可以不给出实际参数,而按指定的默认值进行工作。例如:
voiddelay(intloops=1000)//函数定义,1000为形参loops的默认值 { if(0==loops)return; for(inti=0;i<loops;i++);//空循环,起延时作用 }2.1.4函数的默认形参值这样,当有调用delay(); //和delay(1000)等效程序就会自动将loops当作成1000的默认值来进行处理。当然,也可在函数调用时指定相应的实际的参数值,例如:delay(2000); //形参loops的值为20002.1.4函数的默认形参值在设置函数的默认形参值时要注意:(1)当函数既有原型声明又有定义时,默认参数只能在原型声明中指定,而不能在函数定义中指定。例如: voiddelay(intloops); //函数原型声明 //… voiddelay(intloops=1000)//错误:此时不能函数定义中指定默认参数 { //… }2.1.4函数的默认形参值(2)当一个函数中需要有多个默认参数时,则形参分布中,默认参数应严格从右到左逐次定义和指定,中间不能跳开。例如: voiddisplay(inta,intb,intc=3); //合法 voiddisplay(inta,intb=2,intc=3);//合法 voiddisplay(inta=1,intb=2,intc=3); //合法:可以对所有的参数设置默认值 voiddisplay(inta,intb=2,intc);//错误:默认参数应从最右边开始 voiddisplay(inta=1,intb=2,intc); //错误:默认参数应从最右边开始 voiddisplay(inta=1,intb,intc=3);//错误:多个默认参数中间不能有非默认参数2.1.4函数的默认形参值(3)当带有默认参数的函数调用时,系统按从左到右的顺序将实参与形参结合。当实参的数目不足时,系统将按同样的顺序用声明或定义中的默认值来补齐所缺少的参数。例如:2.1.4函数的默认形参值[例Ex_Default]在函数定义中设置多个默认参数#include<iostream>usingnamespacestd;voiddisplay(inta,intb=2,intc=3)//在函数定义中设置默认参数{ cout<<"a="<<a<<",b="<<b<<",c="<<c<<"\n";}intmain(){
display(1); display(1,5); display(1,7,9); return0;}程序的运行结果为:2.2C++函数特性
在C++中,函数还有:嵌套调用、重载、内联调用以及递归调用等特性,相应的函数被称为嵌套函数、重载函数、内联函数和递归调用等。2.2C++函数特性
函数重载(overloaded)是C++对C的扩展,它允许多个同名的函数存在,但同名的各个函数的形参必须有区别:要么形参的个数不同;要么形参的个数相同,但参数类型有所不同。优点:代码中使用函数的重载,不仅方便函数名的记忆,而且更主要的是完善了同一个函数的代码功能,给调用带来了许多方便。下例程序中即是各种形式的sum函数都称为sum的重载函数。
2.2C++函数特性【例Ex_OverLoad】编程求两个或三个操作数之和#include<iostream.h>
intsum(intx,inty);intsum(intx,inty,intz);doublesum(doublex,doubley);doublesum(doublex,doubley,doublez);
//声明4个同名的函数intmain(){ cout<<sum(2,5)<<endl; //结果为7 cout<<sum(2,5,7)<<endl; //结果为14 cout<<sum(1.2,5.0,7.5)<<endl;//结果为13.7 return0;}程序的运行结果为:
2.2C++函数特性intsum(intx,inty){ returnx+y; }intsum(intx,inty,intz){ returnx+y+z; }doublesum(doublex,doubley){ returnx+y; }doublesum(doublex,doubley,doublez){ returnx+y+z; }程序运行结果如下:2.2C++函数特性需要说明的是:(1)重载函数必须具有不同的参数个数或不同的参数类型,若只有返回值的类型不同是不行的。例如:voidfun(inta,intb); intfun(inta,intb);是错误的。因为如果有函数调用fun(2,3)时,编译器无法准确地确定应调用哪一个函数。(2)当函数的重载带有默认参数时,也要应该注意避免上述的二义性情况。例如:intfun(inta,intb=0);intfun(inta);是错误的。因为如果有函数调用fun(2)时,编译器也是无法准确地确定应调用哪一个函数。2.2.2函数嵌套调用C++允许在函数中再调用其他函数,这种调用称为函数的嵌套调用。
[例Ex_Root]函数嵌套调用:求解一元二次方程的根。(见书35页)本例main函数中调用了root函数,root函数中又调用了sdelta和print函数,而sdelta函数还调用了cmath头文件定义的库函数sqrt(求平方根)和fabs(求浮点数的绝对值)。它们的调用关系如图2.2所示。main函数root函数sdelta函数fabs库函数print函数sqrt库函数图2.2例Ex_Root中的各函数调用的关系2.2.3递归函数C++允许在调用一个函数的过程中出现直接地或间接地调用函数本身,这种情况称为函数的递归调用。递归(Recursion)是一种常用的程序方法(算法),相应的函数称为递归函数例如,用递归函数编程求n的阶乘n!。n!=n*(n-1)*(n-2)*...*2*1。它也可用下式表示:由于n!和(n-1)!都是同一个问题的求解,因此可将n!用递归函数longfactorial(intn)来描述,程序代码如下:
2.2.3递归函数[例Ex_Factorial]编程求n的阶乘n!#include<iostream>usingnamespacestd;longfactorial(intn);intmain(){ cout<<factorial(4)<<endl; //结果为24 return0;}
longfactorial(intn){ longresult=0; if(0==n) result=1; else result=n*factorial(n-1); //进行自身调用 returnresult;}1.5.4函数的递归调用程序运行结果如下:24下面来分析main函数中“factorial(4);”语句的执行过程,这一过程用图1.8来表示:见书(p36)2.2.4内联函数在程序的执行过程中,调用函数时首先需要保存主调函数的现场和返回地址,然后程序转移到被调函数的起始地址继续执行。被调函数执行结束后,先恢复主调函数的现场,取出返回地址,并将返回值赋给函数调用本身,最后在返回地址处开始继续执行。当函数体比较小且执行的功能比较简单时,这种函数调用方式的系统开销相对较大。
2.2.4内联函数为了解决这一问题,C++引入了内联函数的概念,它把函数体的代码直接插入到调用处,将调用函数的方式改为顺序执行、直接插入的程序代码,这样可以减少程序的执行时间,但同时增加了代码的实际长度。内联函数的使用方法与一般函数相同,只是在内联函数定义时,需在函数的类型前面加上inline关键字。例如:2.2.4内联函数[例Ex_Inline]用内联函数实现求两个实数的最大值#include<iostream>usingnamespacestd;
inlinefloatfmax(floatx,floaty){ returnx>y?x:y; }intmain(){ floata; a=fmax(5,10); //A cout<<"最大的数为:"<<a<<"\n"; return0;}这样,当程序编译时,A语句变成了:a=5>10?5:10;2.2.4内联函数在使用内联函数时,还需要注意的是:(1)内联函数也要遵循定义在前,调用在后的原则。形参与实参之间的关系与一般函数相同。(2)关键字inline必须放在函数定义体前才是内联函数,仅在函数声明时使用inline,不能定义内联函数。(3)在C++中,需要定义成的内联函数不能含有循环、switch和复杂嵌套的if语句。(4)递归函数不能被用来做内联函数。总之,内联函数一般是比较小的、经常被调用的、大多可在一行写完的函数。2.3作用域和存储类型
2.3.1作用域作用域又称作用范围,是指程序中标识符(变量名、函数名、数组名、类名、对象名等)的有效范围。一个标识符是否可以被引用,称之为标识符的可见性。在一个C++程序项目中,一个标识符只能在声明或定义它的范围内可见,在此之外是不可见的。根据标识符的作用范围,可将其作用域分为5种:函数原型作用域、函数作用域、块作用域、类作用域和文件作用域。其中,类作用域将在以后介绍,这里介绍其他几种。2.3作用域和存储类型1.函数原型作用域函数原型作用域指的是在声明函数原型所指定的参数标识符的作用范围。这个作用范围是在函数原型声明中的左、右圆括号之间。
正因为如此,在函数原型中声明的标识符可以与函数定义中说明的标识符名称不同。由于所声明的标识符与该函数的定义及调用无关,所以可以在函数原型声明中只作参数的类型声明,而省略参数名。例如: doublemax(doublex,doubley); 和 doublemax(double,double); 是等价的。2.3.1作用域2块作用域块语句就是由“{”和“}”组成的(复合语句)。在块中声明的标识符,其作用域从声明处开始,一直到结束块的花括号为止。块作用域也称作局部作用域,具有块作用域的变量是局部变量。例如:voidfun(void){a的作用域b的作用域 inta; //a的作用域起始处 cin>>a; if(a<0){ a=-a;
intb; //b的作用域起始处 … } //b的作用域终止处} //a的作用域终止处2.3.1作用域需要说明的是:当标识符的作用域完全相同时,不允许出现相同的标识符名。而当标识符具有不同的作用域时,却允许标识符同名。例如: voidfun(void)块A块B { //块A开始 inti; … { //块B开始 inti;
i=100; … } //块B结束 }
//块A结束代码中,在A和B块中都声明了变量i,这是允许的,因为块A和块B不是同一个作用域。块A块B2.3.1作用域但同时出现另外一个问题,语句“i=100;”中的i是使用A块中的变量i还是使用B中的变量i?C++规定在这种作用域嵌套的情况下,如果内层(块B)和外层(块A)作用域声明了同名的标识符,那么在外层作用域中声明的标识符对于该内层作用域是不可见的。也就是说,在块B声明的变量i与块A声明的变量i无关,当块B中的i=100时,不会影响块A中变量i的值。2.3.1作用域4.文件作用域在所有函数外定义的标识符称为全局标识符。全局标识符的作用域是文件作用域。它从声明之处开始,直到文件结束一直是可见的。具有文件作用域的变量和常量称为全局变量和全局常量。例如:
constfloatPI=3.14; //全局常量PI,其作用域从此开始 inta; //全局变量a,其作用域从此开始 voidmain() { //… } voidfunA(intx) { //… }2.3.1作用域5函数作用域若函数定义在后,调用在前,必须进行函数原型声明。若函数定义在前,调用在后,函数定义包含了函数的原型声明。一旦声明了函数原型,函数标识符的作用域是从声明或定义之处开始到源程序文件结束。例如:
voidfunA(intx);//函数funA的作用域从此开始到文件结束
voidfunB() //函数funB的作用域从此开始到文件结束
{ //… } voidmain() { //… } voidfunA(intx) { //… }2.3.3存储类型存储类型是针对变量而言的,它规定了变量的生存期。无论是全局变量还是局部变量,编译系统往往根据其存储方式定义、分配和释放相应的内存空间。变量的存储类型反映了变量在哪开辟内存空间以及占用内存空间的有效期限。在C++中,变量有4种存储类型:自动类型、静态类型、寄存器类型和外部类型,这些存储类型是在变量定义时来指定的.其一般格式如下:<存储类型><数据类型><变量名表>;
2.3.3存储类型1.自动存储类型一般说来,用自动存储类型声明的变量都是限制在某个程序范围内使用,即为局部变量。从系统角度来说,自动存储类型变量是采用动态分配方式在栈区中来分配内存空间。因此,当程序执行到超出该变量的作用域时,就释放它所占用的内存空间,其值也随之消失了。在C++语言中,声明一个自动存储类型的变量是在变量类型前加上关键字auto,例如:
auto inti;2.3.3存储类型若自动存储类型的变量是在函数内或语句块中声明的,则可省略关键字auto,例如: voidfun() {
inti; //省略auto //… }2.3.3存储类型2寄存器类型使用关键字register声明寄存器类型的变量的目的是将所声明的变量放入寄存器内,从而加快程序的运行速度。例如:
registerinti; //声明寄存器类型变量但有时,在使用register声明时,若系统寄存器已经被其他数据占据时,寄存器类型的变量就会自动当作auto变量。2.3.3存储类型2.静态类型从变量的生存期来说,一个变量的存储空间可以是永久的,即在程序运行期间该变量一直存在,如全局变量。也可以是临时的,如局部变量,当流程执行到它的说明语句时,系统为其在栈区中动态分配一个临时的内存空间,并在它的作用域中有效,一旦流程超出该变量的作用域时,就释放它所占用的内存空间,其值也随之消失。
2.3.3存储类型但是,若在声明局部变量类型前面加上关键字static,则将其定义成了一个静态类型的变量。这样的变量虽具有局部变量的作用域,但由于它是用静态分配方式在静态数据区中来分配内存空间。因此,在这种方式下,只要程序还在继续执行,静态类型变量的值就一直有效,不会随它所在的函数或语句块的结束而消失。简单地说,静态类型的局部变量虽具有局部变量的作用域,但却有全局变量的生存期。2.3.3存储类型[例Ex_Static]使用静态类型的局部变量#include<iostream>usingnamespacestd;voidcount(){ inti=0;
staticintj=0; //静态类型 i++; j++; cout<<"i="<<i<<",j="<<j<<"\n";}intmain(){ count(); count(); return0;}
2.3.3存储类型程序中,当第1次调用函数count时,由于变量j是静态类型,因此其初值设为0后不再进行初始化,执行j++后,j值为1,并一直有效。第2次调用函数count时,由于j已分配内存且进行过初始化,因此语句“staticintj=0;”被跳过,执行j++后,j值为2。故程序运行结果为:
2.3.3存储类型需要说明的是,静态类型的局部变量只在第一次执行时进行初始化,正因为如此,在声明静态类型变量时一定要指定其初值,若没有指定,编译器还会将其初值置为0。2.4名称空间
随着程序代码的增多,名称相互冲突的可能性也将增加。尤其是在程序中使用多家厂商提供的类库时,标示符名称的冲突可能性更高。例如,两个类库中可能都定义了名为List、Tree和Node的类,但定义的含义和方式不兼容;或是两个类库定义基个程序本相同,但两个类库同时包含在一个程序中,会出现标示符重复定义的错误。这些问题统称为名称空间问题。解决这些名称空间问题的方法是使用C++新的名称空间特性,通过名称空间的作用域机制来解决。2.4名称空间2.4.1名称空间的定义在C++中,定义一个名称空间的格式如下:其中,namespace是C++关键字,标识符用作名称空间的名称,属于该名称空间体中的变量、函数、结构、枚举、联合以及以后要讨论的类等都可以认为是该名称空间的成员。namespace[标识符]{成员;…}体2.4.1名称空间的定义需要说明的是:同一个文件中,可以允许定义多个名称空间。一旦定义名称空间后,标识符就标识名称空间体的那个区域。例如:
usingnamespacestd; namespaceDING1 { charname[]="thisisinDING1region!"; voidshowname(void) {
cout<<name<<endl; } }2.4.1名称空间的定义 namespaceDING2 { charname[]="thisisinDING2region!"; voidshowname(void) { cout<<name<<endl; } }
尽管名称空间DIN1和DING2中定义的成员名都相同,但它们由于分属不同的名称区域,因而是合法的。此后,在引用各自的成员时就可使用域作用运算符来指定DIN1或DING2来标识各自所在的名称区域。2.4.1名称空间的定义(2)名称空间中的函数、类等可称为成员函数和成员类。成员函数的原型必须在名称空间体中声明,而此时的函数定义称为函数的实现,可以在体内完成,也可在体外完成。例如,前面名称空间DING1和DING2的成员函数showname的声明和定义都是在体内一起完成的,若将成员函数showname的实现放在体外完成,则必须用域作用符“::”指明其所属名称空间。(下页)2.4.1名称空间的定义例如: usingnamespacestd;
namespaceDING1 { charname[]="thisisinDING1region!"; voidshow
温馨提示
- 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
- 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
- 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
- 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
- 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
- 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
- 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。
最新文档
- 二零二五年度厨师餐饮行业厨艺大赛组织合同
- 2025年度股东协议补充协议:针对公司分立、合并后的股权处理
- 二零二五年度数字经济干股股份合作协议
- 第二单元第四节《视频声音显效果-插入音视频超链接》教学设计 2023-2024学年西交大版(2014)初中信息技术七年级下册
- 二零二五年度全球供应链风险管理合作协议
- 二零二五年度智能化停车服务车位租赁协议
- 2025年度时尚品牌店铺经营合伙协议书
- 2025年鹤壁职业技术学院单招职业倾向性测试题库汇编
- 《移动网络规划与优化》课件 项目三 移动网络规划 任务1 了解无线网络规划
- 第三单元 第四课 草原人家(第2课时 现代化牧场)教学设计-人教版历史与社会七年级上册
- 无痛病房管理课件
- 让孩子变成学习的天使——由《第56号教室的奇迹》读书分享
- 球泡检验标准
- 公安笔录模板之询问嫌疑人(书面传唤治安案件)
- 振动分析基础讲义1
- 记账凭证汇总表excel模板
- 邓丽君经典歌曲30首简谱(共33页)
- 故障诊断技术的国内外发展现状(共3页)
- 园林绿化施工通用表格模板
- 初中《生物》(人教版)实验目录表
- 人民检察院信访案件终结办法
评论
0/150
提交评论