函数参数传递三种方式(传值方式,地址传递,引用传递)

形参与实参
形参即形式上的参数,对实参的一种抽象类型描述只是声明一个函数能够接受什么类型的实参,而不确定接受的实参的具体内容是什么
实参即传递给函数对应形参的具体内容

参数传递两种方式–传值&传址
传值方式:将实参的值拷贝给函数或方法,在函数内对形参进行操作,操作的对象是实参的拷贝,对实参本身没有影响,在函数结束返回后,形参被丢弃释放,实参的内容不会被改变
传址方式:将实参的地址传递给函数,在函数内对形参进行操作等同于对实参进行相同的操作,在函数调用结束返回后,形参被释放,实参的内容是对形参进行操作后的结果。
传址又分为:引用传递&指针传递

1 值传递



exchange1(a,b);在调用过程中,函数隐含的把实参a,b的值分别传给了x,y 函数中的操作只是对,xy的操作,没有对a,b 进行任何操作,因此啊,ab的值是不变的。

2 地址传递(指针)


 将ab的地址传递给函数,对*px,*py的操作即是对a,b变量本身的操作。可以实现a,b的值交换

3 引用传递:

  • 引用传递是以引用为参数,则既可以使得对形参的任何操作都能改变相应数据,又使函数调用方便。引用传递是在形参调用前加入引用运算符“&”。引用为实参的别名,和实参是同一个变量,则他们的值也相同,该引用改变则它的实参也改变。


 仅形式参数的格式与值传递不同,内部定义域调用与值传递完全相同,可以实现ab值得对调

引用传递做函数参数”是C++的特性,C语言不支持。上面的第三个引用传递的例子,一般c语言是线定义指针变量int *p,赋值时候采用p=&a。C++才可以直接使用引用传递,普通变量在形参中使用取地址符号&

C++函数的三种传递方式为:值传递、指针传递和引用传递 - 杜聪聪 - 博客园C++函数的三种传递方式为:值传递、指针传递和引用传递 值传递: 指针传递: 引用传递: 1.值传递:有一个形参向函数所属的栈拷贝数据的过程,如果值传递的对象是类对象或是大的结构体对象,将耗费一定的时https://www.cnblogs.com/ducongcong/p/6912164.html

因为在x,y 前有一个取地址符号&,在调用exchang3(a,b)时会用替换x,y,称xy引用了变量ab,在函数内部便是对实参ab进行操作了,函数 内部可以直接修改a,b的值。

引用即对象的别名,传对象的引用,用于把一个对象的地址作为参数传递过去,而不是对象本身。引用传递,避免了一次实参到形参的拷贝,提高了效率。

1 如果数据对象较小,如内置数据类型或小型结构,则按值传递
2 如果数据对象是数组,则使用指针,并将指针声明为指向const的指针
3 如果数据对象是较大的结构,则使用const指针或const引用,以提高运行效率,以减少复制结构所需要的时间和空间
4 如果数据对象是类对象,则使用const引用,传递类对象参数的标准方式是按引用传递。
   
    如果出现类似的manmin(&x),其中x为int类型的值,那么函数将修改变量x的值。

数组特殊之处(其实不特殊,就是传址模式,和指针变量做参数原理一样,形参中数组元素改变,实参中的数组元素也改变,前提就是指针变量本身的的地址在形参和实参中不能改变,只是传递。改变的是指针变量指向的数据2022.10.8)就是形参和实参同指向一个地址即数组实参的地址,是底层利用指针完成

但不是典型的C++引用传递2022.9.29

https://mbd.baidu.com/ug_share/mbox/4a83aa9e65/share?product=smartapp&tk=b30de1137b1820dc96ab21bb922e257d&share_url=https%3A%2F%2Fyebd1h.smartapps.cn%2Fpages%2Fblog%2Findex%3FblogId%3D117437758%26_swebfr%3D1%26_swebFromHost%3Dbaiduboxapp&domain=mbd.baidu.com

引用传递,是C++非常重要的特性。引用传递能够将变量或对象本身作为参数传递,而不是复制一份副本后,传递副本。

引用传递的主要作用有二

第一,函数内部可修改变量或对象。函数返回后,函数调用者得到的也是被修改后的值。常见场景:① 函数需要返回多个值,由于return只能返回一个值,因此可以将其他值以引用传递的形式修改。② 控制递归过程,可以令参数为引用传递,每次递归执行函数体,就会修改参数,当参数等于某个值时递归结束。

第二,也是最重要的作用,引用传递可以避免对象传递时的复制构造。如果函数参数是对象,且采用值传递,则从调用者传到被调函数的参数,需要调用一次复制构造函数。如果这个对象很大,复制构造的开销就会很高。反观引用传递,它传递对象本身,可以完美规避复制构造的过程,极大减少时空开销。最后,如果不希望函数体更改引用传递的对象,则应在对象参数前加const限定,即const引用传递,如下图,这也是参数传递中最常见的形式。

顺带一提,函数返回对象时,还会进行两次复制构造:先将返回的对象复制到临时空间,这是第一次复制构造;而后释放函数所占空间;最后将临时空间的对象复制到调用者的对象,这是第二次复制构造。这个过程复制构造的代价甚至超过了参数值传递。那么,能否也使用引用传递避免复制构造呢?当然可以,不过这就需要用到C++11最重要的新特性——右值引用。右值引用非常复杂,这里不展开,后面有时间再详细讲解。

既然引用传递这么好,是不是说,只要函数体内不会更改变量值,我们都可以使用引用传递替代值传递呢?回答这个问题之前,我们需要先了解引用传递底层的实现机制。看下面两个函数:

第一个函数是引用传递,而第二个函数,实际上是编译器对第一个函数进行解释的结果。也就是说,引用传递,底层是用一个指针实现的,该指针不可指向其他变量,但指针所指内容可以修改(记不清const和指针关系的,赶紧去补课…)在第一个函数中所有对n的操作,会被翻译为第二个函数中对*ptr_n的操作,二者完全等价。也就是说,C++的引用传递,底层是用指针实现,在传递时会产生一个指针变量的开销。

那么问题来了,对于我们现在普遍使用的64位机,指针变量占空间是8字节。而所有基本类型,长度都小于等于8字节。因此,如果参数是基本类型,引用传递反而比值传递产生了更大的内存开销!因此,如果传递的是基本数据类型,则值传递一般优于引用传递。

总结:如果是传递对象,引用传递可以避免复制构造带来的大量开销,推荐使用;如果是传递基本数据类型,引用传递因其指针带来的开销,反而不如值传递。
————————————————
版权声明:本文为CSDN博主「C is My Best Friend」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/qq_34719392/article/details/101787215

最近又碰到了函数传参的问题,每次碰到都会让作者疑惑一段时间,这次干脆总结整理一下,顺便让自己更加透彻的了解传参的问题。咱们开始吧。

c语言中,函数调用时传送给形参表的实参必须与形参有三个一致:类型、个数、顺序,
函数传参有三种方式:值传递,地址传递,引用传递

下面我们首先来介绍值传递:

值传递为最简单的一种,因为它是把实参的值传送给函数局部工作区相应的副本中,函数使用这个副本执行必要的功能。函数修改的是副本的值,实参的值不变。
我们来看代码:

#include<stdio.h>
void swap(int m, int n){
    int m;
    m = n;
    n = temp;
}
int main(){
    int a, b;
    a = 2;
    b = 3;
    swap(a, b);
    printf("a=%d,b=%d",a,b);
}

打印出结果

我们可以看到,a和b在进行交换之前,他们的值分别为2和3,而进行交换操作之后,它们的值仍为2和3,但是在函数swap内部我们可以看到m和n的值确实发生过了交换。
怎么解释这个问题呢,我们可以利用以下代码:

a = 2;
m = a;
m = m+3;
a = ?;    m = ?;

这时我们可以看出来,a赋值之后又将a的值赋给了m,这样无论m进行什么操作,都不会影响到a的值。
说到这里我们就可以理解上面的代码了
调用参数的时候,swap(a,b)代表把a和b作为参数传入,在函数swap接收的时候,它们会自动的将a和b的值copy一份给与自己的参数,也就是形参。
换成代码可以如下理解:

swap(a,b);
swap(int m = a,int n = b);

所以函数内部进行的所有操作都不会影响到外部。

那么如何才能使得调用函数后实参也发生转换呢?
说到这里,我们就可以引出第二种传参方式:地址传递。

地址传递与值传递的不同在于,它把实参的存储地址传送给形参,使得形参指针和实参指针指向同一块地址。因此,被调用函数中对形参指针所指向的地址中内容的任何改变都会影响到实参。
上代码-.-

#include<stdio.h>
void swap(int *m, int *n){
    int temp;
    temp = *m;
    *m = *n;
    *n = temp;
    printf("m=%d,n=%d\n",m,n);
    printf("m=%d,n=%d\n",*m,*n);
}
int main(){
    int a, b;
    a = 2;
    b = 3;
    printf("交换前:a=%d,b=%d\n",&a,&b);
    swap(&a, &b);
    printf("交换后:a=%d,b=%d\n",a,b);
    printf("交换后:a=%d,b=%d\n",&a,&b);
}


结果如下:

如图所示,a和b的值确实发生了交换,原理是什么呢?为了便于大家更好的理解,我将交换前后ab的地址也一并打印了出来。
我们可以看到,执行swap函数时,m和n都拿到了a和b的地址。其实它们的操作和值传递是一样的,只不过不使用指针时,m和n会将a和b的值放到属于自己的地址中。使用指针后m和n也指向了a与b的地址,再进行交换,这样交换过后就不会局限于之在函数内部。

值得一提的是,如果你在函数中使用了指针变量来交换值,那么最终结果仍会是失败的(和谭浩强第三版C语言中得例10.4一样,交换子函数的形参对应的指针,这是重新分配的形参中副本,他不会改变实参的原来的指针2022.9.29)
如下:

#include<stdio.h>
void swap(int *m, int *n){
    int *temp;
    printf("m=%d,n=%d\n",m,n);
    printf("m=%d,n=%d\n",*m,*n);
    temp = m;
    m = n;
    n = temp;

    printf("m=%d,n=%d\n",m,n);
    printf("m=%d,n=%d\n",*m,*n);
}
int main(){
    int a, b;
    a = 2;
    b = 3;
    printf("交换前:a=%d,b=%d\n",a,b);
    printf("交换前:a=%d,b=%d\n",&a,&b);
    swap(&a, &b);
    printf("交换后:a=%d,b=%d\n",a,b);
    printf("交换后:a=%d,b=%d\n",&a,&b);
}

a与b没有交换成功。
怎么理解?
我又将m和n交换途中的地址进行打印,很明显,我把mn的地址互换了,但是并没有对两个地址对应的值进行操作。你交换成功了 ,但又没有完全成功(不是)。

所以接下来就可以隆重介绍我们的第三位选手:引用传递

引用传递是以引用为参数,则既可以使得对形参的任何操作都能改变相应数据,又使函数调用方便。引用传递是在形参调用前加入引用运算符“&”。引用为实参的别名,和实参是同一个变量,则他们的值也相同,该引用改变则它的实参也改变。
它的代码就方便多了

#include<stdio.h>
void swap(int &m, int &n){
    int temp;
    temp = m;
    m = n;
    n = temp;
    printf("m=%d,n=%d\n",m,n);
    printf("m=%d,n=%d\n",&m,&n);
}
int main(){
    int a, b;
    a = 2;
    b = 3;
    printf("交换前:a=%d,b=%d\n",a,b);
    printf("交换前:a=%d,b=%d\n",&a,&b);
    swap(a, b);
    printf("交换后:a=%d,b=%d\n",a,b);
    printf("交换后:a=%d,b=%d\n",&a,&b);
}

轻松完成任务。

对比于三种传参方式来说,引用传参直接操作实参,比前两种都简单。

引用类型作形参,在内存中并没有产生实参的副本,它直接对实参操作;而一般变量作参数,形参与实参就占用不同的存储单元,所以形参变量的值是实参变量的副本。因此,当参数传递的数据量较大时,用引用比用一般变量传递参数的时间和空间效率都好。

指针参数虽然也能达到与使用引用的效果,但在被调函数中需要重复使用“*指针变量名”的形式进行运算,这很容易产生错误且程序的阅读性较差;另一方面,在主调函数的调用点处,必须用变量的地址作为实参。

那么,本次分享到这里就告一段落了,喜欢的朋友可以给个免费的赞,我们下次再见。
————————————————
版权声明:本文为CSDN博主「刺杀曲奇兔」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/qq_53548177/article/details/122151648

Logo

旨在为数千万中国开发者提供一个无缝且高效的云端环境,以支持学习、使用和贡献开源项目。

更多推荐