提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档
文章目录
前言
传参是c/c++经常用到的,本人在刚开始学习的时候比较困惑,这次在这里总结归纳一下c/c++的各种传参方式
一、一般函数的调用:
函数的调用(参数传递)分为三种:传值调用,传址调用和引用调用,其中引用调用是c++语言中特殊的调用,这种调用是在c语言中没有的。
1.传值调用
(1)特点
调用时系统先计算实参表达式的值,再讲实参按位置赋给形参,也就是对形参的初始化。
(2)实现机制:
系统将实参复制一个副本给形参,在被调用的过程中,形参的改变并不影响实参的改变,也就是说,在内存块中,系统复制实参的数值到另一片内存块,这片内存块就是形参,而如果改变形参的值,也就是改变形参部分内存块的值,对于实参部分内存块并没有什么影响,当函数退出时,形参值将销毁。
(3)代码演示
#include<iostream>
using namespace std;
void swap(int a,int b)
{
int temp;
temp=a;
a=b;
b=temp;
cout<<"a="<<a<<",b="<<b<<endl;
}
int main(void)
{
int a(10);
int b(20);
swap(a,b);
cout<<"a="<<a<<",b="<<b<<endl;
}
结果:
2.传址调用
(1)特点
调用时将实参的地址赋给对应的形参指针,使形参的指针指向实参变量。
(2)实现机制:
传值调用是开辟另一块内存块存储实参的值,而传址调用就是开辟另一块内存块存储实参的地址,也就是说,形参就是实参的地址,通过改变形参,而去改变实参的地址,直接去改变实参的值。
(3)代码演示
#include<iostream>
using namespace std;
void swap(int *a,int *b)
{
int temp;
temp=*a;
*a=*b;
*b=temp;
cout<<"a="<<a<<",b="<<b<<endl; //输出地址
cout<<"a="<<*a<<",b="<<*b<<endl;
}
int main(void)
{
int a(10),b(20);
swap(&a,&b);
cout<<"a="<<a<<",b="<<b<<endl;
system("pause");
return 0;
}
结果:
可以看到第一行输出的是a和b的地址,而通过输出a和b从而输出a和b的值,而传址调用过后实参的值发生了改变;
3.引用调用
(1)特点
引用调用是c++独有的,简单来说就是给实参起一个别名,通过这个别名去改变实参的值。
(2)实现机制:
使用引用调用时,实参要是变量的变量名,将实参的变量名传递给形参引用,相当于形参直接使用了实参,只不过用的是实参的别名,这种传递方式的好处是不用将实参的值或地址复制给另一个内存块,直接改变实参的值,开销非常小,与传址调用相比而言更好,所以在平时敲代码时推荐使用这种调用方法。
(3)代码演示
#include<iostream>
using namespace std;
swap(int &x,int &y)
{
int temp;
temp=x;
x=y;
y=temp;
cout<<"a="<<x<<"b="<<y<<endl;
}
int main(void)
{
int a(20),b(10);
int &c=b; //这样引用,相当于c是b的别名
cout<<&b<<endl;
cout<<&c<<endl; //验证b和c的地址相同
swap(a,b);
cout<<"a="<<a<<"b="<<b<<endl;
system("pause");
return 0;
}
结果:
第一行和第二行验证了b和c的地址相同。
二、使用数组作为函数的参数
下面三种方式是当数组调用时的情况,这三种调用方式结果相同,只是调用的机制不同而已,但是在最近学习过程中发现引用调用在特殊情况有妙用。
1.形参实参都是数组
(1)特点
调用和被调用函数的参数都用数组。
(2)实现机制:
这种调用的机制是形参和实参共用内存中的数组,不复制内存块。
2.形参实参都用对应数组的指针
(1)特点
形参和实参都用指针形式
(2)实现机制:
同1,形参和实参共用内存中的数组,不复制内存块。
(3)代码演示
#include<iostream>
using namespace std;
int a[8]={1,3,5,7,9,11,13};
void fun1(int b[],int n) //第一种调用方式
{
for(int i=0;i<n-1;i++)
{
b[7]+=b[i];
}
}
void fun2(int *b,int n) //第二种调用方式
{
for(int i=0;i<n-1;i++)
{
*(b+7)+=*(b+i);
}
}
int main(void)
{
int m=8;
fun1(a,m);
cout<<"数组前七个元素的和:"<<a[7]<<endl;
fun2(a,m);
cout<<"数组前七个元素之和+第八个元素:"<<a[7]<<endl;
fun2(a,m);
cout<<"数组前七个元素之和+第八个元素:"<<a[7]<<endl;
system("pause");
return 0;
}
结果:
可以看到这两种调用方法直接改变实参数组元素中的值,
3.形参用引用,实参用数组名
(1)特点
用typedef 定义一个数组类型的别名;再通过别名引用。
typedef char Line[81];
Line text,line;
(2)实现机制及代码演示:
刚开始不太清楚这种定义有什么意义,直到在编译过程中遇到一个问题,举个栗子:
::将数组逆置:
比如规定数组a [5]=1 2 3 4 5,要求输出5 4 3 2 1
如果通过调用函数解决问题,那么传参就会有问题(虽然可以不用这样写也能解这个题,但是就是为了举栗子)
#include<iostream>
using namespace std;
void f1(int b[]);
int main(void)
{
int a[5]={1,2,3,4,5};
int end=sizeof(a)/sizeof(a[0])-1;
cout<<"传参前数组长度:"<<end<<endl;
f1(a);
system("pause");
return 0;
}
void f1(int b[])
{
int start=0;
int end=sizeof(b)/sizeof(b[0])-1;
cout<<"传参后数组长度:"<<end<<endl;
while(start<end)
{
swap(b[start],b[end]);
start++;
end--;
}
cout<<"结果: ";
for(int k=0;k<5;k++)
{
cout<<b[k];
}
}
结果:
结果错了,最后发现形参之后是数组第一个元素的地址,但是如果用引用调用就能完美解决问题;
代码:
#include<iostream>
using namespace std;
typedef int arr[5];
void f1(arr &b);
int main(void)
{
int a[5]={1,2,3,4,5};
int end=sizeof(a)/sizeof(a[0])-1;
cout<<end<<endl;
f1(a);
system("pause");
return 0;
}
void f1(arr &b)
{
int start=0;
int end=sizeof(b)/sizeof(b[0])-1;
cout<<end<<endl;
while(start<end)
{
swap(b[start],b[end]);
start++;
end--;
}
for(int k=0;k<5;k++)
{
cout<<b[k];
}
}
这样传参形参的b就是整个数组的字节数而不是第一个元素的字节数。
总结
虽然很简单很基础但是很重要,拿来复习复习也不错。