关于变量的值传递还是引用传递;

一 golang
结论:golang中都是采用值传递,即拷贝传递,也就是深拷贝。没有引用传递。之所有有些看起来像是引用传递的场景,是因为Golang中存在着引用类型,如slice、map、channel、function、pointer这些天生就是指针的类型,在传递的时候是复制的地址。引用类型作为参数时,称为浅拷贝,形参改变,实参数跟随变化。因为传递的是地址,形参和实参都指向同一块地址。(切片是引用类型,数组是值类型)
例子:


package main
import (
 "fmt"
)

func modify(fp *Person) {
 fp.Age = 80
}

func modifyValue(fp Person) {
 fp.Age = 80
}

type AutoC struct {
 URL string `json:"url"`
 Name int `json:"name"`
}

func main() {
 p := Person{
 Age:10,
 Name:"wang",
 }
 modifyValue(p)
 fmt.Println(p) //结果不变
 modify(&p) 
 fmt.Println(p) //结果改变
}

上面结果可以看出,默认的结构体传递是深拷贝,是值传递,而如果强制使用引用传递,也是可以的;对于slice、map、channel、function、pointer默认就是指针类型,这些传的值其实是『引用』,所以在代码中容易出现case;

二 python

python其实最好理解,

python中统一都是引用传递,同时要注意类型是属于对象的,而不是变量。而对象有两种,“可更改”(mutable)与“不可更改”(immutable)对象。在python中,strings, tuples, 和numbers是不可更改的对象,而list,dict等则是可以修改的对象。
“不可更改”的对象
当我们写下面语句时:

a = “hello world”
Python解释器其实顺序干了两件事情:

在内存中创建一个字符串“hello world”;
在内存中创建一个名为“a”的变量,并将“a”指向字符串“hello world”(将“hello world”的地址保存到“a”中)。
这样我们就能通过操作“a”而改变内存中的“hello world”。
a = “123”
b = a
a = “xyz”
执行第一句Python解释器创建字符串“123”和变量“a”,并把“a”指向“123”。
执行第二句,因为“a”已经存在,并不会创建新的对象,但会创建变量“b”,并把“b”指向“a”指向的字符串“123“。 b和a同时指向“123”
执行第三句,首先会创建字符串“xyz”,然后把“xyz”的地址赋予“a“(“a”指向字符串“xyz”)。
“可更改”的对象
a = [1, 2, 3]
b = a
a[0], a[1], a[2] = 4, 5, 6 //改变原来 list 中的元素
>>> a
[4, 5, 6]
>>> b
[4, 5, 6]
从这里可以看出strings类型是不可变量,不可变实际上指的是不会更该字符串,比如把a = ‘123’ 变为 a =’1234′ 实际上是先创建了 “1234” 再用a去指向它。
但是,像list,dict等“可更改”的变量,他们会直接再本地更改,不会进行副本拷贝。
简言之,当在 Python 中 a = sth 应该理解为给 sth 贴上了一个标签 a。当再赋值给 a 的时候,就好象把 a 这个标签从原来的 sth 上拿下来,贴到其他对象上,建立新的”引用”。

既然Python只允许引用传递,那有没有办法可以让两个变量不再指向同一内存地址呢?

浅拷贝(copy)和深拷贝(deepcopy)
import copy
a = [1, 2, 3, 4, [‘a’, ‘b’]]#原始对象

b = a #赋值,传对象的引用
c = copy.copy(a) #对象拷贝,浅拷贝
d = copy.deepcopy(a) #对象拷贝,深拷贝

a.append(5) #修改对象a

a[4].append(‘c’) #修改对象a中的[‘a’, ‘b’]数组对象

print ‘a = ‘, a
print ‘b = ‘, b
print ‘c = ‘, c
print ‘d = ‘, d

输出结果:
a = [1, 2, 3, 4, [‘a’, ‘b’, ‘c’], 5]
b = [1, 2, 3, 4, [‘a’, ‘b’, ‘c’], 5]
c = [1, 2, 3, 4, [‘a’, ‘b’, ‘c’]]
d = [1, 2, 3, 4, [‘a’, ‘b’]]
copy对于一个复杂对象的子对象并不会完全复制,什么是复杂对象的子对象呢?就比如序列里的嵌套序列,字典里的嵌套序列等都是复杂对象的子对象。对于子对象,python会把它当作一个公共镜像存储起来,所有对他的复制都被当成一个引用,所以说当其中一个引用将镜像改变了之后另一个引用使用镜像的时候镜像已经被改变了。

deepcopy的时候会将复杂对象的每一层复制一个单独的个体出来。 当然其中主要的操作还是地址问题。

函数参数传递
a = 1
def fun(a):
a = 2
fun(a)
print(a) # 1
a = []
def fun(a):
a.append(1)
fun(a)
print(a) # [1]
当一个引用传递给函数的时候,函数自动复制一份引用,这个函数里的引用和外边的引用没有半毛关系了.所以第一个例子里函数把引用指向了一个不可变对象,当函数返回的时候,外面的引用没半毛感觉.而第二个例子就不一样了,函数内的引用指向的是可变对象,对它的操作就和定位了指针地址一样,在内存里进行修改.

Python垃圾回收机制中的“引用”
引用计数
PyObject是每个对象必有的内容,其中ob_refcnt就是做为引用计数。当一个对象有新的引用时,它的ob_refcnt就会增加,当引用它的对象被删除,它的ob_refcnt就会减少.引用计数为0时,该对象生命就结束了。

优点:

1.简单
2.实时性
缺点:

1.维护引用计数消耗资源
2.循环引用
三 JAVA
java也是值传递
当传的是基本类型时,传的是值的拷贝,对拷贝变量的修改不影响原变量;当传的是引用类型时,传的是引用地址的拷贝,但是拷贝的地址和真实地址指向的都是同一个真实数据,因此可以修改原变量中的值;当传的是String类型时,虽然拷贝的也是引用地址,指向的是同一个数据,但是String的值不能被修改,因此无法修改原变量中的值。

首先来解释一下什么是引用传递,什么是值传递。

引用传递(pass by reference)是指在调用方法时将实际参数的地址直接传递到方法中,那么在方法中对参数所进行的修改,将影响到实际参数。

值传递(pass by value)是指在调用方法时将实际参数拷贝一份传递到方法中,这样在方法中如果对参数进行修改,将不会影响到实际参数。

所谓的值类型指的是在赋值时,直接在栈中(Java 虚拟机栈)生成值的类型。
二值类型和引用类型
值类型:整数型 浮点型 字符类型 布尔类型
所谓的值类型指的是在赋值时;直接在栈中;Java 虚拟机栈)生成值的类型
引用类型是指除值类型之外的数据类型
类、接口 数组 字符串包装类 所谓的引用类型是指在初始化时将引用生成栈上;而值生成在堆上的这些数据类型。
注意这里跟golang的区别,类,数组均为引用类型;golang中切片是引用,struct本身不是,struct如果包含了map或者切片,也是引用类型;
四 php
php万能的array均为深拷贝,除非特意标明&引用传递,否则均不会改变原值,这里做的比较统一和容易理解;

发表评论

电子邮件地址不会被公开。 必填项已用*标注


*