}
int max(int x,int y)
{
if(x>y)
return x;
else return y;
}
2、 函数的递归调用:函数直接或间接地调用自身,则称为递归调用。
例题:设计函数fact(n),计算并返回n的阶乘。
四、函数原形与头文件:
1、函数原形(无函数体,以分号结尾,可省略形参)
函数原型是一条函数说明语句,一条函数原型语句说明了一个函数的接口(函数名、函数参数的个数、参数类型及返回值类型)
2、函数原形:延伸了函数的作用域
a) 必须使用函数原型的场合:
i. 多文件程序;
ii. 函数之间的递归调用;
C++中,函数之间完全可以随意进行相互调用,函数funcA ()调用了函数funcB(),而funcB中又调用了函数funcA(),这时就必须在开始说明后定义的函数原形,以便在先定义的函数中能够调用。
iii. 使用函数库;
使用自己定义的函数原形。
3、 头文件:
为了便于提供函数调用所需的接口信息,程序文件中定义的函数的原形通常都记录、保存在头文件中,每个程序文件对应一个头文件。
四、程序运行时的内存分布:(P83)
当一个应用程序被启动执行时,操作系统会给该程序分配一块内存空间,用于存放该程序运行过程中的数据,包括程序本身的执行代码、程序中定义的各种常量、变量等。
该内存可分为四个区域:程序代码区、全局数据区、堆和栈。
程序代码区 |
存放函数编译成的二进制代码 |
全局数据区 |
存放定义的全局变量以及函数体中定义的静态变量 |
堆 |
是由程序支配的内存空间。在程序运行的初期,堆是一块空闲的区域,在程序运行过程中,可以用new在堆中动态申请空间,当用完了所申请的空间以后,就可以用delete释放这一块堆空间,然后这块空间就可以被重新利用了 |
栈 |
由系统使用的动态空间,在进行函数调用时,系统使用栈来存放传递给被调用函数的信息,并且在栈中为被调用函数的局部变量分配空间。当被调用函数执行结束时,为调用这个函数所分配的所有空间就会被系统自动释放。 |
五、函数调用的实现机制:
从栈的变化角度进行讲解:
函数的调用和返回遵循的规律与栈的后进先出的特征相吻合。在每次进行函数调用时,需开辟一定的内存空间存放如下信息:
1、 当前函数的运行状态和返回地址;
2、 被调用函数的参数;
注:栈空间由系统管理的,函数之间并不能互相访问局部量。
提醒:由于内存空间是有限的,所以,为了确保内存的有效利用,在不再需要所申请的内存空间的时候,应及时释放它们。
六、函数的参数和返回值:
除不能返回数组外,函数的参数和返回值可以是任意数据类型。
七、函数调用中的参数传递:P122
(一)、传递方式:(三种)
传递方式 |
特 点 |
按值传递 |
A、形参与实参占一个独立的存储空间。 |
B、形参的存储空间是函数被调用时才分配的。调用开始,系统为形参开辟一个临时存储区,然后将各实参之值传递给形参,这时形参就得到了实参的值,这种方式称为按值传递。 |
C、函数返回时,临时空间被释放。 |
D、对形参的修改,不会影响到实参的值。 |
按地址传递 |
|
按引用传递 |
A、引用是它所引用的变量或常量的一个别名(生成一个对实在参数的别名)。 |
B、当函数的形式参数是引用类型时,它实际上是对实在参数所代表的变量或常量的引用,自己不具有独立的内存空间 |
(二)
1、 数组参数:实际上是一个指向实在参数的数组元素的指针。
在使用数组传递参数时,一般会同时将数组元素的个数作为参数传递给函数。并且,在函数中,对数组参数做的改变将会影响到实在参数。
2、 指针参数:将实参的地址传递给形参。
3、 引用参数:自己不具有独立的内存空间,函数的调用可直接用变量名。
4、字符串作参数:
当用字符串作参数时,不必将字符串的长度传递给函数,因为字符串是以空字符‘\0‘结尾,在函数中可通过判断空字符来决定字符串是否结束。
5、多维数组作参数
(三)可选参数:P126
是关于参数中有默认值的问题。
C++中允许为函数的参数指定默认值。指定的方法是在函数的参数表中为要指定默认值的参数直接赋值。
八、内联函数:
一种特殊类型的函数,在定义或声明时在前面加上“inline”关键字。
inline int max(a,b)
{
return(a>b)?a:b;
}
C++编译器处理内联函数的特殊性:
在遇到调用内联函数的地方会用函数体中的代码来替换函数的调用,比如:
int maxinum=max(val1,val2);等价于:
int maxinum=((val1>val2)?val1:val2);
也就是说,程序执行时并没有真正调用函数max(),而是将内联函数的函数体中的语句直接在函数调用的地方展开了。
九、函数重载:P129
函数名相同,功能相似,参数不同。也就是说,函数名重载是指同一作用域内的多个函数使用相同的函数名,这些同名函数通过它们各自不同的参数表进行区分
1、函数重载的条件:
如果几个函数完成的功能类似,但是所操作的数据类型不同,就可以将它们用同一个名字来命名,而用不同的参数来区分不同的函数,即使用函数重载。
2、函数重载的使用方法:
A、重载函数的区分是以函数参数来进行的,而不是用函数的返回值来区分不同的函数,所以参数表完全相同而返回值不同的两个同名函数在C++中不认为是重载,而判定为错误的定义。
B、不要让功能不同的函数进行重载。
十、函数和变量的作用域:
作用域就是标识符在程序中能使用的范围。C++中的作用域与标识符的声明位置密切相关,一个标识符的作用域一般开始于标识符的声明处,而作用域的结束位置取决于标识符声明在程序中的位置。
根据标识符作用的范围,可将作用域分为文件作用域、局部作用域和类作用域。
1、函数的作用域:通常是全局的,不但在定义它的文件中可调用,而且在同一应用系统的其它程序文件中也可以调用。
还有一种函数仅仅是为了提供给同一文件中的函数调用,这种函数属于静态函数,其作用域称为文件作用域。
2、举例
//演示:文件作用域
//___________________
#include //cin,cout的作用域开始
double factorial(int n) // factorial的作用域开始
{
…..
}
void main() //main的作用域开始
{
cin>>n; //在cin的作用域中
…
fact= factorial(n) //在factorial的作用域中
…
cout<
…
} //cin,cout,factorial和main的作用域结束
2、变量的作用域和生存期:
依据变量作用域的不同,变量可分为全局变量和局部变量。
A、全局变量:定义于函数外部的变量。
B、局部变量:定义于函数内部的变量,其作用域具有局部作用域。
标识符的声明出现在函数的定义中。
分为三种局部变量:
(1) 自动变量:具有自动生存期,从变量定义处开始,到所在块运行结束时为止。
(2) 寄存器变量:p133
(3) 静态局部变量:具有静态生存期。
(4) 举例:
//从键盘读入一个整数n,计算并输出n!
//使用goto语句
//__________________
#include
double factorial(int n) //n作用域开始
{
double retVal=1; //retVal作用域开始
for(int i;i<=n;i++) //i
retVal*=i; //i作用域结束
return retVal;
} //n,retVal作用域结束
void main()
{ //END作用域开始
double fact; //fact作用域开始
do{
cout<<"please intput n:";
int n; //n作用域开始
cin>>n;
if(n==0)
goto END;
fact=factorial(n);
cout<
}while(1); //n作用域结束
END:
cout<<"thank you!\n";
} //fact,END作用域结束
十一、函数模板:(P123)
利用函数重载可以让具有类似功能而参数不同的函数使用相同的名字,这样程序更易理解,但仍然要写多个函数,并且这些函数的代码几乎完全相同,怎样才能减少重复代码?用C++的函数模板可进一步提高程序代码的可重复利用程度。只要实现一个函数模板,就可以用这个模板生成许多具体的函数来。(看书中模板样例P124)
使用函数模板使得我们只要写一次代码就可以生成多个具体的函数。
由于模板不限制使用的数据类型,使得我们可以用任何数据类型,甚至是自己定义的类型来享受系统提供的各种功能。
1、模板的实例化:
通过声明函数原型来告诉编译程序实例化函数模板:
int max(int,int);
float max(float,float);
当编译器看到函数原型时,会先生成相应的函数代码。
2、支持多种类型的模板:
在创建函数模板时,可以指定多于一个需要实例化的类型
阅读(1505) | 评论(0) | 转发(0) |