C++参数传递
关于 *和&符号、指针和地址 的基础知识可以查看此篇:[C/C++]C/C++中 *和&、指针和地址 的基本概念
1. 基本概念
- 函数调用时要将主调函数实参的值对应传递给形参
- 函数调用时传给形参表的实参必须与形参三个一致:类型、个数、顺序
- 参数传递两种方式
- 传值方式(参数为整型、实型、字符型等)
传递的是变量的值
传递以后,只把实参值赋值给形参。形参发生了改变,实参的值不会发生改变。 - 传地址
- 参数为指针变量
- 参数为引用类型(C语言没有,C++有)
- 参数为数组名
数组名也是地址,数组名里面存放的数组的基地址/首元素地址
- 传值方式(参数为整型、实型、字符型等)
2. 传值方式
- 代码示例
void swap(int m, int n){
int temp;
temp = m;
m = n;
n = temp;
}
void main(){
int a, b;
cin >> a >> b;
swap(a, b);
cout << a << endl << b <<endl;
}
-
逐行解析
swap(a, b);
void swap(int m, int n){......}
a和b作为实参,传递的时候,对应的将a的值传递给m,将b的值传递给n。m和n就是形参。
在被调用的函数swap()中,交换m和n的值。
swap()函数执行完毕以后,m和n就被释放了,返回到main()函数调用的地方继续执行。a和b的值没有发生任何变化。 -
总结
- 传递的是参数的值;
- 实参和形参各用各的空间;
- 形参发生改变,实参的值不发生改变。
3. 传地址方式
3.1 指针变量做参数
3.1.1 形参变化影响实参
- 代码示例
void swap(int *m, int *n){
int temp;
temp = *m;
*m = *n;
*n = temp;
}
void main(){
int a, b;
int *p1, *p2;
cin >> a >> b;
p1 = &a;
p2 = &b;
swap(p1, p2);
cout << a << endl << b <<endl;
}
- 逐行解析
int a, b;
int *p1, *p2;
定义两个指针p1和p2
cin >> a >> b;
p1 = &a;
p1指向a,p1指针型变量存储的是a的地址
p2 = &b;
p2指向b,p2指针型变量存储的是b的地址
swap(p1, p2);
void swap(int *m, int *n){......}
将p1和p2这两个指针作为实参传递,传递给两个指针变量m和n
p1的值传递给m,p1存储的是a的地址,所以m也存储a的地址,m也指向a;
p2的值传递给n,p2存储的是b的地址,所以n也存储b的地址,n也指向b。
temp = *m;
*m = *n;
*n = temp;
*m是取m指针变量指向的存储单元的内容,也就是a变量的值;同理*n是取b变量的内容。
交换指针m和指针n所指的存储单元的值,即a变量和b变量的值。
交换完之后swap()函数执行完毕,释放形参,m和n就没有了。返回到main()中调用的地方,再输出a和b值的时候,a和b的值发生了变化。
3.1.2 形参变化不影响实参
- 代码示例
void swap(int *m, int *n){
int *temp;
temp = m;
m = n;
n = temp;
}
void main(){
int a, b;
int *p1, *p2;
cin >> a >> b;
p1 = &a;
p2 = &b;
swap(p1, p2);
cout << a << endl << b <<endl;
}
- 逐行解析
main函数部分与3.1.1中的一致
p1指向a,p2指向b
将p1传递给m,m存放a的地址;p2传递给n,n存放b的地址。则m也指向a,n也指向b
int *temp;
定义一个指针变量temp
temp = m;
指针变量temp存放m的值,m存放的是a的地址,所以temp也存放a的地址,即temp也指向a
m = n;
n指向b,同理m指向b
n = temp;
temp指向a,同理n指向a
如上操作即交换了m和n指向的存储单元,对a和b没有任何影响。
所以swap()函数调用完毕,m和n被释放了,返回到main()中调用的地方,a和b的值没有任何影响。
3.2 数组名作参数
- 代码示例
//void swap(int a[]){
void swap(int * a){
int temp;
temp = a[1];
a[1] = a[0];
a[0] = temp;
}
void main(){
int num[2] = {3, 4};
swap(num);
cout << num[0] << endl << num[1] << endl;
}
-
逐行解析
int num[2] = {3, 4};
数组名字存的是数组中第一个元素的地址,称作数组的首地址。
swap(num);
将数组名作为参数传递,实际上是传递一个地址,传递的是数组的首地址。
void swap(int * a)
用地址(指针存的是一个地址)存放地址(传过来的数组的首地址)
这时候实际上就是把整个数组传递过去了。因为数组第一个元素传递过去了,下一个元素就可以找到。
所以对形参数组所做的任何改变都将反映到实参数组中。
void swap(int a[])
传递过来一个地址,用数组名a保存这个传递过来的地址,这个[]括号里不能指定大小。
temp = a[1];
a[1] = a[0];
a[0] = temp;
在被调用的函数中对形参数组进行操作,当这个操作结束也就是给a[0]和a[1]交换了值。
所以调用结束后,输出a[0]=4,a[1]=3,实参数组num的结果发生改变。 -
总结
- 数组名存的是数组的首地址
- 传递数组名实际上传递的是数组的首地址
- 传递数组的首地址来传递整个数组;
- 对形参数组操作实际上就是对实参数组操作。
3.3 引用类型作参数(C语言没有,C++有)
-
引用
引用是C++语法
它用来给一个对象提供一个替代的名字
引用相当于是一个别名
int &j = i;
定义 j 是 i 的引用, j 就是 i 的别名
操作 i 和操作 j 是一模一样的。i 的值改变, j 的值也改变;j 的值改变, i 的值也改变。
另一个角度理解:
&是地址,可以理解为 j 和 i 的地址是一样的。也就是说他们共用同一个空间,是同一个东西,所以其中一个改变了,另一个也跟着改变。 -
代码示例
void swap(int & m, int & n){
int temp;
temp = m;
m = n;
n = temp;
}
void main(){
int a, b;
cin >> a >> b;
swap(a, b);
cout << a << endl << b << endl;
}
-
逐行解析
swap(a, b);
void swap(int & m, int & n)
m是对a的引用;n是对b的引用。
m和a用的是同一块空间;n和b用的是同一块空间。
所以对m的任何操作实际上就是对a的操作;对m的任何操作实际上就是对a的操作。
temp = m;
m = n;
n = temp;
以上交换了m和n的值,实际上也是交换了a和b的值。
所以对形参m和n的操作相当于对实参a和b的操作。
-
总结
- 引用本质就是取一个别名;
- 引用类型作参数时其实用的是同一块空间;
- 对引用类型的形参的操作相当于对其实参的操作。
4. 三点说明
- 传递引用类型给函数与传递指针的效果是一样的,形参变化实参也发生变化
- 引用类型作形参,在内存中并没有产生实参的副本,它直接对实参操作;
而一般变量作参数,形参和实参就占用不同的存储单元,所以形参变量的值是实参变量的副本。
因此,当参数传递的数据较大时,用引用比用一般变量传递参数的时间和空间效率都好。在C++的参数传递中,推荐使用引用类型作为参数。 - 指针参数虽然也能达到使用引用的效果,但在被调函数中需要重复使用“*指针变量名”的形式进行运算,这很容易产生错误且程序的阅读性比较差;
另一方面,在主调函数的调用点处,必须用变量的地址作为实参。