先看下面这个例子,用来求出两个int类型数的最大公约数:
int gcd(int v1, int v2)
{
while (v2) {
int temp = v2;
v2 = v1 % v2;
v1 = temp;
}
return v1;
}
int main () {
int a = 10, b = 12;
gcd(a,b);
return 0;
}
我们可以看出c++语言使用调用操作符(也就是一对圆括号)实现函数的调用,针对上面的函数调用:首先在MAIN函数中执行gcd()函数,这时将实参(a和b)传给gcd,同时main被挂起,并保存现场,开始执行gcd的函数块. 然后是将 int v1=a;int v2=b; 这样用实参去初始化函数gcd的形参,但是要注意的是这里面的v1, v2和实参a,b并不是同一块内存.v1, v2只是在gcd函数代码块内部创建的临时变量,当代码块结束,他们将自动释放.
参数传递的几种情况
1.非引用形参
*上面的例子就是普通的非引用形参,也就是用实参的副本初始化形参,函数并不会访问到实参的内存,所以不会修改实参的值.
*还有就是指针形参,例如:
void reset (int *p) {
*p = 0;
p = 0;
}
int main () {
int i = 42;
int *q = &i;
reset(q);
}
在上面的例子中, 我们使用了一个指向int型的指针q传递给reset()函数,
这时有 int *p = q; 也就是说我们的实参q是指向int型变量i所在的内存的,现在我们用 q初始化形参p,也就是将q所指向的地址付给了p,所以形参p现在也指向i的内存, 所以*p=0; 也就是将i内存中的值修改为0, p=0;是将指针制空,不指向任何内存. 我们可以看出指针形参跟普通的形参一样,都是是实参的副本初始化形参,调用后不会改变实参的值, 但是不同点是,由于指针本身没有任何意义,只有将它与带有值得一块内存绑定了它才有意义,所以指针形参,会修改实参所指向的内存中的值.
*const形参
上面的指针形参的例子,如果我们不想让形参指针修改实参指针所指向内存地址的值怎么办呢?
看下面例子:
void use_ptr(const int *p) {
*p = 0; // error:这是错误的,因为p是const类型的,值不能被修改
int a = *p; // ok: 赋值是可以的,只能拿来用,但是不能修改
p = 0;
}
可以看出,我们可以将形参设置为const类型,这样由于const的特性,形参p就只能拿来用,不能被修改了.值得注意的是, 形参是const还是非const将影响到我们的实参,根据指针的初始化改则, 可以将const类型的指针初始化成const和非const都可以,但是非const得指针只能用非const初始化.
总结 上面的这3中情况,都是复制实参的例子,但是有些情况是不适合复制实参的:
*当需要在函数修改实参的值时;
*当需要以大型对象作为实参传递时.对实际应用而言,复制实参所付出的时间和存储空间过大.
*当没有办法实现对象的复制时.
2.引用形参
面对上面的问题,这时就要用到引用形参了,我们举个最经典的例子:
void swap (int &v1, int &v2) {
int temp = v2;
v2 = v1 ;
v1 = temp;
}
int main () {
int a= 10, b = 12;
swap(a,b);
return 0;
}
这里面的原理是 int &v1 = a; int &v2 = b; 我们都知道引用&其实就是给变量起个别名,但是具有变量的功能,所以这里面v1,v2其实指向的就是 a,b的内存,对他们的操作 就是对a,b操作,所以这里面不用复制实参来初始化形参,而是直接操作实参的内存.就可以起到上面的作用了.
引用形参 也可以有const类型的,也就是 只能拿来用,不能修改.
指针类型的,可以起到改变的值得作用.
基本跟上面差不多,这里不做详细介绍. 谢谢.