关于 Go method 参数 传值与传址的疑问

在 本地 Ubuntu 测试环境下,写了两个简单的程序,分别为 exchange1.go exchange2.go,对于 go 语言中,方法传值和传址有疑问,望大家帮我分析解答一下,谢谢。

1. 运行环境

go version go1.16.4 linux/amd64

2. 代码

2.1 exchange1.go 以及运行结果
package main

import (
    "fmt"
)

// 切片类型
type intArr []int

func (arr intArr) swap(i, j int) {
    arr[i], arr[j] = arr[j], arr[i]
}

func main() {
    list := intArr{1, 2, 3, 4, 5}
    fmt.Println("原始数据:", list)
    list.swap(0, 4)
    fmt.Println("处理后的数据:", list)
}

执行命令:go run exchange1.go, 得到如下结果:

原始数据: [1 2 3 4 5]
处理后的数据: [5 2 3 4 1]

可以看到该切片的第一位和最后一位互换了位置。

2.2 exchange2.go 程序及运行结果
package main

import (
    "fmt"
)
// 数组类型
type intArr [5]int

func (arr intArr) swap(i, j int) {
    arr[i], arr[j] = arr[j], arr[i]
}

func main() {
    list := intArr{1, 2, 3, 4, 5}
    fmt.Println("原始数据:", list)
    list.swap(0, 4)
    fmt.Println("处理后的数据:", list)
}

执行命令: go run exchange2.go, 得到如下结果

原始数据: [1 2 3 4 5]
处理后的数据: [1 2 3 4 5]

可以看到,数组的第一位和最后一位没有互换位置。

3. 我的疑问:

在为类型绑定方法的时候,所有的接收者(receiver) 都是采用的值类型的方式。为什么执行完 exchange1.go 后,会改变原始数据的排序呢?而在执行完 exchange2.go 后,原始数据没有变化。

最佳答案

@Wi1dcard 这个大佬说的对,切片是引用数组的内存地址,所以改变后值也改变,而数组这种值类型会拷贝一个新的数组,所以不会影响原数组。

补充一下, go函数参数的传递方式

在Go语言中,函数参数都是以复制的方式(不支持以引用的方式)传递(比较特殊的是,Go语言闭包函数对外部变量是以引用的方式使用)。 — << Go语言高级编程 >>

来验证一下我们的猜想

package main

import (
    "fmt"
)

// 数组类型
type intArr [5]int

func swap(list [5]int) {
    list[1] = 99
}

func main() {
    list := intArr{1, 2, 3, 4, 5}
    swap(list)
    fmt.Println(list)
}

//此时数据没有改变 [1 2 3 4 5]
package main

import (
    "fmt"
)

// 切片类型
type intArr []int //本身就有一个数组的内存地址

func swap(list []int) { //接收的数据也是这个数组的内存地址
    list[1] = 99
}

func main() {
    list := intArr{1, 2, 3, 4, 5}
    swap(list)
    fmt.Println(list)
}

//此时数据发生改变 [1 99 3 4 5]

切片指针都是指向同一个底层数组

多谢大佬指正

文章参考:segmentfault.com/a/119000001524618...

2年前 评论
joylee109 (楼主) 2年前
讨论数量: 3

我的理解是,数组本身是值类型的,而切片是数组的上层表示,所有操作都会影响到原数组,所以会出现这种结果。

2年前 评论
joylee109 (楼主) 2年前

数组接收者值传递,修改的不是原本数组的地址

2年前 评论
joylee109 (楼主) 2年前

@Wi1dcard 这个大佬说的对,切片是引用数组的内存地址,所以改变后值也改变,而数组这种值类型会拷贝一个新的数组,所以不会影响原数组。

补充一下, go函数参数的传递方式

在Go语言中,函数参数都是以复制的方式(不支持以引用的方式)传递(比较特殊的是,Go语言闭包函数对外部变量是以引用的方式使用)。 — << Go语言高级编程 >>

来验证一下我们的猜想

package main

import (
    "fmt"
)

// 数组类型
type intArr [5]int

func swap(list [5]int) {
    list[1] = 99
}

func main() {
    list := intArr{1, 2, 3, 4, 5}
    swap(list)
    fmt.Println(list)
}

//此时数据没有改变 [1 2 3 4 5]
package main

import (
    "fmt"
)

// 切片类型
type intArr []int //本身就有一个数组的内存地址

func swap(list []int) { //接收的数据也是这个数组的内存地址
    list[1] = 99
}

func main() {
    list := intArr{1, 2, 3, 4, 5}
    swap(list)
    fmt.Println(list)
}

//此时数据发生改变 [1 99 3 4 5]

切片指针都是指向同一个底层数组

多谢大佬指正

文章参考:segmentfault.com/a/119000001524618...

2年前 评论
joylee109 (楼主) 2年前

讨论应以学习和精进为目的。请勿发布不友善或者负能量的内容,与人为善,比聪明更重要!