关于go语言编译器自动解引用
- 用值类型的实参调用形参为值类型的方法
- 用指针类型的实参调用形参为值类型的方法(会进行“自动解引用”)
- 用值类型的实参调用形参为指针类型的方法(会进行“自动取引用”)
- 用指针类型的实参调用形参为指针类型的方法
我们还是用代码来说明问题。
type node struct {
Name string
Next *node
}
// 用c语言翻译,
// void Say(node* n) { printf("%d
", 123); }
func (n *node) Say() {
fmt.Println(123)
}
// 用c语言翻译,
// void Say(node n) { printf("%d
", 123); }
func (n node) Say2() {
fmt.Println(2333)
}
func main() {
n1 := &node{Name: "hui"}
n1.Say()
n1.Say2() // Say2()是值接收器 (*n1).Say2() go编译器为我们加上* 自动解引用
n2 := node{Name: "hui"}
n2.Say() // Say()是指针接收器 (&n2).Say() go编译器为我们加上& 自动取引用
n2.Say2()
}
记住两点
-
go语言只有值传递,指针也是值传递。
-
值类型接收器、指针类型接收器 可以理解为函数 的 一个形参。结合c语言如何做,对于c语言来说传值、传指针哪种会改变实参。
关键理解:当传递给方法的形参是一个值类型时(值类型接收器),原值的内容不会受到影响,因为值传递,传递进来的是原值的复制副本(形参是副本);相反,当方法的形参是一个指针类型时,原值的内容将可能受到影响,因为传递的是指针类型复制,虽然是复制但是指针中存储的地址仍然是实参的真实地址。
一个链表节点删除的例子
以链表节点删除问题为例,由于在go语言中都是参数都是值传递,所以在链表操作的时候不能直接操作形参来修改原来指针的内容,但可以修改其指向的内容为什么呢,用代码来解释
type node struct {
Name string
Next *node
}
func main() {
n1 := &node{Name: "zgh"}
n2 := &node{Name: "dev"}
n3 := &node{Name: "devaaa"}
n1.Next = n2
n2.Next = n3
n3.Next = nil
ts(n2)
data := make([]string, 0)
for n1 != nil {
data = append(data, n1.Name)
n1 = n1.Next
}
fmt.Println(data)
}
func ts(n *node) {
// 方式一: out: [hui dev devaaa] 不会删除
n = n.Next // 这里的n是一个指针的复制,此操作只会更改形参n指针的指向,不会更改实参的指向,如果想修改实参的指向,只能用方式二
// 方式二: out: [hui devaaa]
//*n = *n.Next // 会更改实参,也就是被复制的指针
// 方式三: out: [hui asada]
// 这种方式会更改实参,为什么呢 因为这里go语言的编译器会为你加上*来解引用
//n.Next= n.Next.Next // 实际(*n).Next=
//n.Name = "asada" // 实际(*n).Name=
}