在使用了多种语言之后,发现不同语言之间,参数传递的方式有很多细微的差异,一不小心就会在这方面踩坑,今天我们就来聊一聊java,python和go语言中具体的参数传递方式和其特点。
首先来了解一下编程语言中参数传递有哪些方式
1.值传递2.引用传递
什么是值传递?什么是引用传递?
值传递:将原变量内容复制下来,再用一个新的内存空间来保存,两个变量之间相互独立; 函数范围内对值的任何改变在函数外部都会被忽略, 系统将为目标对象重新开辟一个完全相同的内存空间;
引用传递:给当前变量起了一个别名,实际上这两个变量引用的是一个值。相当于给一个变量起了两个名字;函数范围内对值的任何改变在函数外部也能反映出这些修改, 它们指向同一内存空间;
其实,无论是值传递
还是引用传递
,传递给函数的都是变量的副本
,不同的是值传递的是值的拷贝
,引用传递的是地址的拷贝
,地址拷贝效率高,因为数据量小,而值拷贝
决定于数据量的大小,数据越大,效率越低
。
值传递和引用传递
下面使用一个示例来演示一下值传递
1.java示例
public class test { public static void main(String[] args) { int n = 3; System.out.println("Before change, n = " + n); change(n); System.out.println("After change(n), n = " + n); } public static void change(int nn) { nn = 10; }}
// resultBefore change, n = 3After change(n), n = 3
1.go示例
type example struct { s string}func main() { ns := example{s: "hello"} fmt.Printf("before change %v, ptr:%p\n", ns, &ns) change(ns) fmt.Printf("after change %v, ptr:%p\n", ns, &ns)}func change(example example) { example.s = "hello world" fmt.Printf("changing %v, ptr:%p\n", example, &example)}
// resultbefore change {hello}, ptr:0xc000010200changing {hello world}, ptr:0xc000010220after change {hello}, ptr:0xc000010200
1.python2.7示例
def change(a): a = 20if __name__ == "__main__": a = 10 print("before change, "+str(a)) change(a) print("after change, "+str(a))
# resultbefore change, 10after change, 10
上面3个例子可以发现,都使用的是值传递,change
方法执行后并不会影响外部的原始变量
效果如图
总结一下
•java,在基本类型为(byte,short,int,long,double,float,char,boolean)时为值传递•go,在类型为非指针的任意类型里,都是使用的值传递•python,在类型为不可变对象(比如str,int或者tuple),不能直接修改原始对象——相当于通过“传值”来传递对象
那么哪些时候使用的是引用传递呢?
1.java示例
public class test2 { public static void main(String[] args) { StringBuffer sb = new StringBuffer("Hello "); System.out.println("Before change, sb = " + sb); change(sb); System.out.println("After change(n), sb = " + sb); } public static void change(StringBuffer strBuf) { strBuf.append("World!"); }}
// resultBefore change, sb = Hello After change(n), sb = Hello World!
1.go示例
func main(){ s := "hello" fmt.Printf("before change %s, ptr:%p\n",s, &s) change(&s) fmt.Printf("after change %s, ptr:%p\n",s, &s)}func change(s *string) { *s = *s + " world" fmt.Printf("changing %s, ptr:%p\n",*s, s)}
// resultbefore change hello, ptr:0xc00003a1f0changing hello world,ptr:0xc00003a1f0after change hello world, ptr:0xc00003a1f0
1.python2.7示例
def change(l): l.append("world")if __name__ == "__main__": l = ["hello"] print("before change, ", l) change(l) print("after change, ",l)
# result('before change, ', ['hello'])('after change, ', ['hello', 'world'])
示例发现,执行change
方法后,外部的变量也发生的改变,所以使用的是引用传递
效果如图
总结
•java,对象类型(Object,数组,容器)均为引用传递•go,当类型为指针时,为引用传递•python,当为可变对象(map, list)的引用,就能修改对象的原始值——相当于通过“传引用”来传递对象。
java和python传递场景类型,基础类型使用值传递,对象使用引用传递;go传递方式与类型无关,只与是否是指针有关
值传递和引用传递特点
值传递
1.必须复制值,特别时对于大型的字符串和对象来说,将会是一个代价很大的操作2.复制的新值和原始变量相互独立,互不影响3.值传递使用的基础类型一般都分配在栈空间中,不需要gc,提升性能
引用传递
1.引用传递不用复制值,对性能提高很有好处2.引用传递的对象或者指针一般分配到堆内存中,当频繁创建销毁,gc会大大降低性能