main.go
package main
import "fmt"
// 指针(任何类型的指针)、slice和map作为返回值
// 当带有指针的返回值被赋给外部变量或者作为参数传递给其他函数时, 编译器无法确定该变量何时停止使用
// 因此,为了确保安全性和正确性,它必须将该数据分配在堆上,并使其逃离当前函数作用域
func f1() (*int, []int, map[int]int) {
i := 0
list := []int{1, 2, 3, 4}
mp := map[int]int{1: 1, 2: 2}
return &i, list, mp
}
// 向chan中发送数据的指针或者包含指针的值。会//编译器此时不知道值什么时候会被接收,因此只能放入堆中J
func f2() {
i := 2
ch := make(chan *int, 2)
ch <- &i
<-ch
}
// 非直接的函数调用,比如在闭包中引用包外的值,因为闭包执行的生命周期可能会超过函数周期,因此需要放入堆中
func f3() func() {
i := 1
return func() {
fmt.Println(i)
}
}
// 在slice或map中存储指针或者包含指针的值
// sLice和map都需要动态分配内存来保存数据。当我们将一个指针或包含指针的值放入sLice或map时,
// 编译器无法确定该指针所引用的数据是否会在函数返回庙仍然被使用
// 为了保证数据的有效性,编译器会将其分配到堆上,以便在函数返回后继续存在
func f4() {
i := 1
list := make([]*int, 10)
list[0] = &i
}
// interface类型多态的应用,可能会导致逃迎
// 由于按口类型可以持有任意实现了该按口的类型,编译器在编译时无法确定具体的动态类型
// 因此,为了保证程序正确性,在运行时需要将接口对象分配到堆上
func f5() {
var a animal = dog{}
a.run()
var a1 animal
a1 = dog{}
a1.run()
}
// 由于fmt打印是interface类型
// 如果输出的内容包含指针或者channel,那么可能会发生内存泄露
func f6() {
a := &dog{}
fmt.Println(a)
}
type animal interface {
run()
}
type dog struct{}
func (a dog) run() {}