函数如果使用参数,该参数变量称为函数的形参。形参就像定义在函数体内的局部变量。调用函数,可以通过两种方式来传递参数。即值传递和引用传递,或者叫做传值和传引用。


Ø 值传递(传值)

值传递是指在调用函数时将实际参数复制一份传递到函数中,这样在函数中如果对参数进行修改,将不会影响到原内容数据。

默认情况下,Go 语言使用的是值传递,即在调用过程中不会影响到原内容数据。

每次调用函数,都将实参拷贝一份再传递到函数中。每次都拷贝一份,性能会下降,但是 Go语言中使用指针和值传递配合就避免了性能降低问题,也就是通过传指针参数来解决实参拷贝的问题。

Ø 引用传递(传引用)

引用传递是指在调用函数时将实际参数的地址传递到函数中,那么在函数中对参数所进行的修改,将影响到原内容数据。

严格来说Go语言只有值传递这一种传参方式,Go语言是没有引用传递的。

Go语言中可以借助传指针来实现引用传递的效果。函数参数使用指针参数,传参的时候其实拷贝一份指针参数,也就是拷贝了一份变量地址。

函数的参数如果是指针,当函数调用时,虽然参数仍然是按拷贝传递的,但是此时仅仅只是拷贝一个指针,也就是一个内存地址,这样就不用担心实参拷贝造成的内存浪费、时间开销、性能降低的情况。

引用传递的作用如下。

l 传指针使得多个函数能操作同一个对象。

l 传指针更轻量级 (8bytes),只需要传内存地址。如果参数是非指针参数,那么值传递的过程中,每次在拷贝上面就会花费相对较多的系统开销(内存和时间)。所以当要传递大的结构体的时候,用指针是一个明智的选择。

Go语言中slice、map、chan类型的实现机制都是类似指针,所以可以直接传递,而不必取地址后传递指针。

函数传int类型的值与引用的对比,如例所示。

函数传slice类型的值与引用的对比,如例所示。

函数传数组,其类型的值与引用的对比,如例所示。

函数传结构体,其类型的值与引用的对比,如例所示。

Ø 值传递和引用传递细节问题

Go语言中所有的传参都是值传递(传值),都是一个副本、一个复制。

复制的内容有时候是值类型(int、string、bool、数组、struct属于值类型),这样就在函数中就无法修改原内容数据;

有的是引用类型(指针、slice、map、chan属于引用类型),这样就可以修改原内容数据。

是否可以修改原内容数据,和传值、传引用没有必然的关系。在C++中,传引用肯定是可以修改原内容数据的,在Go语言里,虽然只有传值,但是也可以修改原内容数据,因为参数可以是引用类型。

传引用和引用类型是两个概念。虽然Go语言只有传值一种方式,但是可以通过传引用类型变量达到跟传引用一样的效果。