你真的搞懂了参数传递方式吗?(多图超详细)
引入
在学习编程语言的过程中,我相信大多数人都遇到或者经历过一个问题,有的时候你把变量传入了一个方法,经过方法内部的一顿操作之后,发现那个变量并没有发生变化。如果是这样也就算了,关键是有时候你传入的变量经过方法中的操作后它又发生了变化。这是啥情况?😂莫非每次向方法中传入参数都是一场豪赌吗?还是遇事不决量子力学,莫非是那股神奇的力量?😎好吧扯远了,回归正题,归根结底是因为你没有彻底的弄明白编程语言的参数传递过程!
参数传递的两种方式
我相信大多数人都知道参数的两种传递方式:
按值调用
(call by value)按引用调用
(call by reference)
这个针对大多数的编程语言是成立的,当然也有例外,例如:c++还有一个按指针传递(不过都可以按如上这两种理解)。
在很久很久之前还有一个按名调用(call by name)
不过大家放心,现在已经弃用(( •̀ ω •́ )✧ 虚晃一枪)
按值调用
表示方法接收的是调用者提供的值。
按引用调用
表示方法接收的是调用者提供的变量地址。
讲到这里有人可能会说这些我都知道,确实很多人都知道,但也只知道这些了,没有深入的理解,如此的话相当于还是没懂。
接下来上菜!🤞
深入理解按值调用
在这里我们以java语言为例
Java程序设计语言总是采用按值调用。也就是说,方法得到的是所有参数值的一个副本。 ------《Java核心技术 卷Ⅰ》
这句话说的相当的透彻,但是俺还是要提几句。什么是参数值?参数值就是你那个变量里面到底装的是个啥玩楞。怎么样够通俗吧😎。那么什么是副本?副本这个说法太高大上了,我们换个说法分身,这个知道吧,就是孙悟空用毫毛变的那个分身,分身终归是分身,它并不是本体!
我们再合起来理解这句话,也就是说按值调用,就是把变量里面装的那个东西传到方法的形参,自己真身还在外面,自己的分身进到方法里去了。
现在我们来举个例子(为了各种语言使用者都能理解,以下使用我的自创语言,跳出三界之外,不在五行之中(●ˇ∀ˇ●)):
方法A(形参){把传进来的数加个2} //定义方法A
a = 2
方法A(a) //把a传进方法A
- 1
- 2
- 3
a这个变量里面装的就是2,他把自己的2复制(分身),传到了方法中(可以说给了形参),这个方法是对他这个分身做出一系列操作,随着方法的执行结束,他的分身也就被五马分尸直接拜拜了,跟他的本体没有啥关系。
来张图解析一下:
我们都知道对于基本数据类型,变量里面装的是一个具体的结果;而对于引用数据类型,变量里面装的是地址。有些人错误的认为只要是传了个引用数据进方法,那就是按引用调用的参数传递方式。大错特错🤦♂️,接下来我们就看一下对于引用数据类型的值传递。
引用数据类型的按值调用
还是用我自创的语言,为大家举个例子:
方法A(形参x){对传入的对象的属性进行改动}
a = 某类的对象
方法A(a)
- 1
- 2
- 3
还记得我们的方法论吗?
按值调用,就是把变量里面装的那个东西传到方法的形参,自己真身还在外面,自己的分身进到方法里去了。
同样的我们也按照上面的方法进行理解。a变量的里面装的是对象的地址,然后a把这个地址复制一份给了形参x,这个分身进入方法中完成属性的改动。
如此一套猛如虎的操作进行下来,一看最后结果傻了眼。不是说进去的是分身,怎么a的属性变了?因为当a把自己的地址给了形参x之后,形参x也拥有了一个指向对象的指针,有了这条指针,当然可以进行改动!
看图!(〃 ̄︶ ̄)人( ̄︶ ̄〃):
纵使形参x在方法执行完毕之后会被回收,但是a指向的对象还在,他造成的影响也还在。
按引用调用
按引用调用,很多人第一反应,就是传地址,这没错,但是关键点在于传什么的地址。很多人不明白按引用调用的本质,就是因为这个地方没搞明白。
给你举个例子:
现在有一个变量a,里面装着某个对象的地址
将变量a传入方法,倘若是按引用调用,传地址,传哪个?这就是引用调用的核心!
按引用调用
表示方法接收的是调用者提供的变量地址。
也就是说给形参的是那个0x1122的地址,而这个0x1122就是变量a在内存中的地址值,换句话说按引用调用相当于直接把真身放到方法中去,这回是孙悟空亲自出山,而不是他的分身了!(~ ̄(OO) ̄)ブ
还没有完全搞懂?没事👌,接下来我们通过一个证明来加深理解!
证明:在java中总是值传递
此实验参考《Java编程思想》
首先我们编写一个交换两个Employee对象的方法:
public static void swap(Employee x,Employee y){
Employee temp = x;
x = y;
y = temp;
}
- 1
- 2
- 3
- 4
- 5
如果Java对对象采用的是按引用调用,那么这个方法就应该实现交换:
var a = new Employee{"Alice",·····};
var b = new Employee{"Bob",······}
swap(a,b);
- 1
- 2
- 3
但是最终失败了,两者没有发生交换。
我们可以看看两种情况的内存解析图:
如果是按值调用:
可以看到任x、y如何乱搞,a、b都纹丝不动,所以最后的结果出来,两者是没有交换的。
如果是按引用调用:
可以看到如果真是按引用调用,那么两者是可以交换的。
文章来源: blog.csdn.net,作者:十八岁讨厌编程,版权归原作者所有,如需转载,请联系作者。
原文链接:blog.csdn.net/zyb18507175502/article/details/122821770
- 点赞
- 收藏
- 关注作者
评论(0)