前言
参数传递机制,是学习编程语言绕不开的一个话题,笔者之前接触 Python 比较多,知道 Python 的参数传递机制实际是赋值传递 ,既不是值传递也不是 引用传递,那么 Go 语言的参数传递机制是啥呢?在揭密之前,我们需要区分 值传递 和 引用传递 两种传递机制的本质差异。
值传递和引用传递的基本区别
-
值传递(Pass-by-Value):
- 当你传递一个变量到函数时,传递的是变量的 副本 (即复制了一份值)。在函数内部对这个副本的操作不会影响到外部原始变量的值。
-
引用传递(Pass-by-Reference):
- 当你传递一个变量到函数时,传递的是变量的 引用 或者说地址(即内存位置)。函数内部对这个引用的操作会直接影响到外部原始变量的值。
Go 中的参数传递机制是值传递
是的,正如标题所说,Go 中的参数传递机制是值传递,那熟悉 Go 编程的同学可能问了,可以传递指向变量的指针,这种情况也是值传递吗?下面我们就解答 这个疑问,这也是笔者学习当中的困惑。
Go 语言的参数传递机制无论传递的是基本类型、结构体、数组还是切片, 本质上都是值传递 。这意味着:
- 传递副本 :即使传递的是指向变量的指针,Go 也会将指针的 副本 传递给函数,而不是传递原始指针本身。这和真正的“引用传递”还是有区别的。
值传递
基本类型的值传递
package main
import "fmt"
func modify(x int) {
x = 10 // 修改副本的值
}
func main() {
a := 5
modify(a)
fmt.Println(a) // 输出 5
}
这段代码中, a
的值 5 被传递到 modify
函数,而 modify
内部的 x
是 a
的 副本 。因此,修改 x
的值并不会影响 a
的原始值。
结构体的值传递
package main
import "fmt"
type Point struct {
x, y int
}
func modify(p Point) {
p.x = 100 // 修改副本的 x 值
}
func main() {
p1 := Point{1, 2}
modify(p1)
fmt.Println(p1) // 输出 {1 2},p1 的值没有改变
}
这段代码中,我们传递的是结构体 Point
,Go 也会传递它的副本。因此, modify
内部修改 p
的值并不会影响到原始的 p1
。
传递指针
如果你传递的是指向变量的指针,那么实际上传递的 依然是指针的副本 ,通过传递指针实现类似“引用传递”的效果,但并不是真正的引用传递, 只要搞清了开篇中值传递 和 引用传递 两种传递机制的本质差异,就不难理解了。
指针的值传递
package main
import "fmt"
func modify(p *int) {
*p = 10 // 修改指针指向的值
}
func main() {
a := 5
modify(&a) // 传递指向 a 的指针
fmt.Println(a) // 输出 10,a 的值被修改
}
这段代码中, &a
是指向 a
的指针的副本,函数内部修改了指针指向的数据,即修改了 a
的值。虽然我们传递的是指针,但 传递的是指针的副本, 因此它依然符合值传递的原则。
最后
- Go 是值传递 :Go 中,无论是基本类型还是指针类型,传递给函数的始终是 副本 。即使你传递了一个指针,函数获得的是指针的副本,但指针指向的内存地址可以被修改,间接影响原始数据。
- 与真正引用传递的区别 :真正的引用传递是传递原始的引用或地址,任何修改都能直接影响到原始变量。在 Go 中,传递的是副本,即便是指针,传递的是指针本身的副本,和“引用传递”在概念上有所不同。