c++中函数参数传递(值传递、指针传递,引用传递)进一步认识
概念
首先从概念上来说一下这几种函数传参方式及区别:
1、值传递:形参是实参的拷贝,改变函数形参的值并不会影响外部实参的值,这是最常用的一种传参方法,也是最简单的一种传参方法,只需要传递参数,返回值那是return考虑的;
2、指针传递:指针传递参数从本质上来说也是值传递,它传递的是一个地址。【值传递过程中,被调函数的形参作为被调函数的局部变量来处理,即在函数内的栈中开辟内存空间以存放由主调函数放进来的实参的值,从而成了实参的一个副本(记住这个,函数内参数的是实参的副本)】。由于指针传递的是外部实参的地址,当被调函数的形参值发生改变时,自然外部实参值也发生改变。
3、引用传递:被调函数的形参虽然也作为局部变量在栈中开辟了内存空间,但是栈中存放的是由主调函数放进的实参变量的地址。被调函数对形参的任何操作都被处理成间接寻址,即通过栈中存放的地址访问主调函数中实参变量(实参和形参通过引用,合二为一,说白点就是:一个人,有两个名字那种;后面想会详细说)。因此,形参的任何改动都会直接影响到实参。
实例
先从简单的例子开始:
- 值传递:例子略过。
- 指针传递:
- 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;
- cout<<"&a=" <<&a<<" ,"<<"&b="<<&b<<endl;
- }
(刚上大学的时候就接触过的例子,交换值)调用:
- int main(){
- int x=1;
- int y=2;
- cout<<"x=" <<x<<" ,"<<"y="<<y<<endl;
- cout<<"&x=" <<&x<<" ,"<<"&y="<<&y<<endl;
- swap(&x,&y);
- }
一定要记住这种调用方式
如指针传递的概念上所述,传地址给形参。
- swap(&x,&y);
形如:int *a = &x;//用于指针传递,a有自己独立的内存地址,存储的内容是x的地址,*a是存x的值。
输出结果:
传入值的各变量的初始状态(地址状态):
从上图关系可以知道:a(b)是一个指向外部实参地址的指针,*a(*b)是指针的内容,如果改变了*a(*b)也必然导致外部实参的改变。
交换后:
*a=2,*b=1;
这样的结果是由于a或者b指针指向x或者y的地址的缘故,因此由于*a,*b值得交换导致外部实参发生变化。
思考一下,下面的操作能否实现值得变化?
简单测试代码:
- int change(char* name){
- cout<<"*******CHANGE--BEFORE******"<<endl;
- cout<<"name=" <<name<<endl;
- cout<<"*name=" <<&name<<endl;
- name="alter";
- cout<<"*******CHANGE--AFTER********"<<endl;
- cout<<"name=" <<name<<endl;
- cout<<"*name=" <<&name<<endl;
- return 1;
- }
- int main()
- {
- char *str = "this is a test";
- cout<<"******MAIN--BEFORE*****"<<endl;
- cout<<"str=" <<str<<endl;
- cout<<"*str=" <<&str<<endl;
- change(str);
- cout<<"*****MAIN--AFTER*****"<<endl;
- cout<<"str=" <<str<<endl;
- cout<<"*str=" <<&str<<endl;
- return 1;
- }
从结果中发现,并未达到改变值得效果,为什么?这个测试代码和本文开始的疑问是一样的,那就进一步分析:
传入值的各变量的初始状态(地址状态):
执行赋值操作
- name="alter";
系统首先需要给字符串“alter”分配内存空间(地址),然后指针才指向其地址。
所以*str并没有发生变化,因此最后打印出来的仍是“this is a test”,这也解释了我开始时的迷惑!
另一种成功传递参数的指针调用方法----指针的指针:
- void my_malloc(void** p, int size)
- {
- *p = malloc(sizeof(int)*size);
- }
- int main()
- {
- int *a;
- my_malloc(&a , 10);
- return 1;
- }
执行结果:(有些参数没有用,只是为了打印出来看看)
当我们没有执行到给*p分配空间的时候:
执行malloc(size) 后的图如下:
赋值给*p后:由于p指向&a即a的地址,*p则指向a的地址里的值,现在又要把分配的内存指向*p,所以,a的值即为新分配的内存!(这个比较难转圈)
然后,我们就给指针a 分配内存成功了。
3、引用传递:
- void swapref(int &a,int &b)
- {
- cout << "******************before swapref:******************"<<endl;
- cout<<"a=" <<a<<" ,"<<"b="<<b<<endl;
- cout<<"&a=" <<&a<<" ,"<<"&b="<<&b<<endl;
- int temp;
- temp=a;
- a=b;
- b=temp;
- cout << "******************after swapref:******************"<<endl;
- cout<<"a=" <<a<<" ,"<<"b="<<b<<endl;
- cout<<"&a=" <<&a<<" ,"<<"&b="<<&b<<endl;
- }
- int main(){
- int x=1;
- int y=2;
- cout<<"******MAIN--BEFORE*****"<<endl;
- cout<<"x=" <<x<<" ,"<<"y="<<y<<endl;
- cout<<"&x=" <<&x<<" ,"<<"&y="<<&y<<endl;
- //swap(&x,&y);
- swapref(x, y);
- cout<<"*****MAIN--AFTER*****"<<endl;
- cout<<"x=" <<x<<" ,"<<"y="<<y<<endl;
- cout<<"&x=" <<&x<<" ,"<<"&y="<<&y<<endl;
- }
一定要记住这种调用方式
- swapref(x, y);
形如:int &a=x; //用于引用传递,可以理解为a就是x,x就是a,只不过名字不一样
执行结果: 这个具体就不分析了,记住引用传递实参和形参是一样的,只是名字不同而已。
总结:
本文重点还是在参数传指针方面,指针确实令人头疼,今天遇到了,会出错,弄明白以后,等过段时间,又忘了,又遇到错误,再来看,这样不断反复,希望能不断的提升,对指针的认识不断的加深!
写了一下午,边琢磨、边参考、边实验,希望对你有用!错误之处敬请见谅!