在 Go 语言中,值拷贝和引用拷贝的概念主要涉及变量赋值、函数参数传递以及返回值。以下是对 Go 中值拷贝和引用拷贝的详细说明:
值拷贝
值拷贝指的是在赋值或函数调用过程中,将一个值的完整副本复制到新变量或函数参数中。对副本的操作不会影响原始值。以下情况会发生值拷贝:
- 基本类型和复合类型(如结构体)的赋值:
type Person struct {
Name string
Age int
}
p1 := Person{"Alice", 30}
p2 := p1 // 值拷贝,p2 是 p1 的完整副本
函数参数传递:
func processPerson(p Person) {
p.Name = "Bob" // 修改函数内的 p 不会影响外部的原始值
}
person := Person{"Alice", 30}
processPerson(person) // 传递 person 时进行值拷贝
- 函数内部对 p 的修改不影响函数外部的 person。
返回值:
func createPerson() Person {
return Person{"Alice", 30} // 返回一个值拷贝给调用方
}
person := createPerson()
引用拷贝(或称为指针拷贝)
引用拷贝指的是在赋值或函数调用过程中,复制的是值的内存地址(即指针),而非值本身。对通过引用拷贝得到的新变量的操作实际上会影响到原始值。以下情况会发生引用拷贝:
- 指针类型的赋值:
p1 := &Person{"Alice", 30}
p2 := p1 // 拷贝指针,p2 和 p1 指向同一块内存
2. 引用类型(切片、map、interface、channel)的赋值:
slice1 := []int{1, 2, 3}
slice2 := slice1 // 切片赋值实际上是引用拷贝,两者共享底层数组
对 slice2 的修改会影响 slice1,因为它们共享同一块底层数组。
函数参数传递:
func modifySlice(s []int) {
s[0] = 42 // 修改函数内的 s 会影响到外部的原始切片
}
slice := []int{1, 2, 3}
modifySlice(slice) // 传递切片时进行引用拷贝
函数内部对 s 的修改会影响函数外部的 slice
特殊情况:切片与结构体
- 切片:虽然切片本身在赋值或传递时是引用拷贝,但它们指向的底层数组是通过值拷贝的方式复制的,除非两个切片共享同一块底层数组。
- 结构体:结构体的赋值和传递默认是值拷贝。但如果结构体中包含指针字段,那么拷贝的是指针本身的值(地址),而不复制指针所指向的数据。因此,对结构体中指针字段的修改会影响到原始结构体中的相应字段。
总结来说,Go 语言中的基本类型、复合类型(如结构体)在赋值和函数参数传递时通常进行值拷贝,而指针类型、切片、map、interface、channel 等引用类型在赋值和传递时进行引用拷贝。理解这些差异有助于编写正确的代码,避免意外的数据共享或修改。
func test() {
x := map[string]string{
"name": "ff",
}
y := x
y["name"] = "zoe"
fmt.Println(">>", x, y)
// >> map[name:zoe] map[name:zoe]
}