1. 函数基础
1.函数的工作原理
函数本质上即为一个命名的代码块,通过函数名来调用函数,从而可以执行相应的代码。函数具有0个或多个参数,运行代码之后(通常)会产生一个结果。
在main
函数内通过函数的名字调用函数,同时将已定义的函数所需的参数(称之为形参)的值(称之为实参)传递给被调用的函数。在函数内部,(隐式地)定义并且使用实参初始化形参,在函数内部一切需要使用实参的地方全都以形参代替。当函数执行结束时,再次隐式地定义并初始化所需返回的值,最终将返回值赋给main
函数中的变量(如果有返回值的话)。
2. 函数的编写与调用
1.函数的编写
典型的函数定义包含以下部分:①返回类型 ②函数名 ③形参列表 ④函数体
[return type] functionName([para type] paraName1,[para type] paraName2,...,[para type] paraNameN)
{
blablablabla; //function body
blabla;
......
return [syntax or value];
}
// An example function to slove the fact of n
int fact(int n,int a)
{
a=1;
if(n<0) return 0;
else if(n==0) return 1;
else if(n==1) return a;
if(num>1)
return fact(n-1,a*n);
}
2.函数的调用
在调用函数时,使用调用运算符(call operator)()
来执行函数。调用运算符作用于函数或指向函数的指针,在圆括号之内则是实参。于是一个典型的调用包含以下部分:①函数名 ②调用运算符 ③实参列表
int main()
{
blablabla;
[para type] paraName1;
[para type] paraName2;
......
[para type] paraNameN;
blablabla;//Initialize all parameter;
[return type] var=functionName(paraName1,paraName2,...,paraNameN);
blablabla;
return 0;
}
3. 参数传递机制
函数具有不同的参数传递机制,这取决于函数的形参列表的类型,具不同类型形参的参数列表决定了不同的形参-实参交互方式。
1.值传递
当初始化一个基本类型的变量或基本类型变量的指针时,初始值按照函数的工作机制将实参拷贝给形参,对于形参的改动不会影响实参的值。
对于一个基本类型变量,拷贝当然不会对初始值有任何影响。而对于一个基本类型指针,形参和实参指向相同的地址,于是对于形参地址存储的值进行修改即是对实参地址存储的值的修改,间接可以达成修改变量的目的,但需要注意的是,实参的地址实际上是一个拷贝,于是在函数体中指针指向的变换不会导致main
函数中实参指向的改变。
2.引用传递
当初始化一个引用时,形参是实参的一个别名,于是任何对于形参的操作实际上是对于实参的操作,形参的地址也是实参的地址,这些与单独的引用类型变量相同的。
3.一个具体的例子
通常在交换两个变量a,b
时,可以采用下面的代码:
temp=a;
a=b;
b=temp;
可以将上面的代码写为两个参数的函数swap(type a,type b)
。利用两种传递机制的定义的三个函数,其目的都是为了交换输入的两个值。代码如下:
#include <iostream>
using namespace std;
void myVSwap(int a, int b)//通过值传递参数的函数
{
int temp = a;
a = b;
b = temp;
cout << a << '\t' << b << endl;
cout << &a << '\t' << &b << endl;
}
void myPSwap(int *a, int *b)//通过指针传递参数的函数
{
int temp = *a;
*a = *b;
*b = temp;
cout << *a << '\t' << *b << endl;
cout << a << '\t' << b << endl;
}
void myRSwap(int &a, int &b)//通过引用传递参数的函数
{
int temp = a;
a = b;
b = temp;
cout << a << '\t' << b << endl;
cout << &a << '\t' << &b << endl;
}
int main()
{
int M = 20, N = 10;
cout << M << '\t' << N << endl;
cout << &M << '\t' << &N << endl; //M和N分别值为20和10
cout << "************** Value **************" << endl;
myVSwap(M, N);
cout << M << '\t' << N << endl;
cout << &M << '\t' << &N << endl;
cout << "************** Pointer **************" << endl;
M = 20, N = 10;
myPSwap(&M, &N);
cout << M << '\t' << N << endl;
cout << &M << '\t' << &N << endl;
cout << "************* Reference *************" << endl;
M = 20, N = 10;
myRSwap(M, N);
cout << M << '\t' << N << endl;
cout << &M << '\t' << &N << endl;
return 0;
}
代码运行的结果如下:
20 10
0050FD08 0050FCFC
************** Value **************
10 20
0050FC24 0050FC28
20 10
0050FD08 0050FCFC
************** Pointer **************
10 20
0050FD08 0050FCFC
10 20
0050FD08 0050FCFC
************* Reference *************
10 20
0050FD08 0050FCFC
10 20
0050FD08 0050FCFC
请按任意键继续. . .
究其原因在于:
①非指针的传值,在函数内部只有形参进行了交换,但是实参没有进行任何操作,于是M和N的值和地址都不改变;
②指针的传值,指针的值也同样没有发生任何操作,但是函数修改了指针的每一个值(地址)对应的变量,于是M和N的值进行了交换,但是地址依旧不变;
③引用的传递,函数内部形参作为实参的别名进行了交换,相当于在对实参进行交换,但对变量值的操作不会改变其地址,因此M和N的值发生了交换,但地址不变。
4. 使用指针和引用传递参数的优势
通常说来,使用指针和引用可以在函数内部修改实参的值,除此之外,还有其他的优势。
- 避免拷贝的作用。拷贝大的类对象或容器对象较为低效,甚至有些类型根本不支持拷贝操作。但也正因为如此,在不需要修改实参值时使用指针和引用传递参数,可能会错误地修改实参,这样只需要将形参定义为对常量的引用或者指向常量的指针即可。
- 返回额外信息。一个函数只有一个返回值,但实际操作中经常需要返回多个值,一种方法是将这些需要返回的值定义为一个新的数据结构,再返回这个数据结构;而采用指针或者引用作为形参,在函数返回之前赋值,这样在函数之外就存储下了变量的值,再返回剩下的一个值即可。