场景一:
strs := []string{"Tom", "Jerry", "Ray"}
for _, v := range strs {
go func() {
fmt.Println(v)
}()
}
//预期想要得到: Tom Jerry Ray
//实际返回:Ray Ray Ray
开启协程是一个异步操作,在协程运行时可能迭代器已经跑完了,并不能保证运行时读到的外部变量值。
场景二:
type student struct {
Name string
Age int
}
func main() {
m := map[string]*student{}
stus := []student{
{Name: "zhou", Age: 24},
{Name: "yin", Age: 23},
{Name: "wang", Age: 22},
}
for _, stu := range stus {
m[stu.Name] = &stu
}
for k, v := range m {
fmt.Println(k, "=", v)
}
}
//预期想要得到:zhou = &{zhou 24} yin = &{yin 23} wang = &{wang 22}
//实际返回:zhou = &{wang 22} yin = &{wang 22} wang = &{wang 22}
循环遍历中的stu是临时变量,每次循环都会重新赋值,&stu指针指向的始终是同一个地址,存放的是stu最新的变量值
场景三:
type People struct{}
type Teacher struct {
People
}
func (p *People) ShowA() {
fmt.Println("people showA")
p.ShowB()
}
func (p *People) ShowB() {
fmt.Println("people showB")
}
func (t *Teacher) ShowB() {
fmt.Println("teacher showB")
}
func main() {
t := Teacher{}
t.ShowA()
}
//实际返回: people showA people showB
golang对象方法中的对象p被显式引用,不会因继承而改变类的指代(严格来讲,go中没
有继承,只有组合)
场景四:
func increaseA() int {
var i int
defer func() {
i++
}()
return i
}
func increaseB() (r int) {
defer func() {
r++
}()
return r
}
fmt.Println(increaseA(), increaseB())
//实际返回:0 1
return,并不是一个原子指令,经过编译之后,变成了三条指令(返回值 = xxx、执行defer
函数、结束当前函数),所以increaseA()的返回值为0
函数显式返回时,return的内容赋值给r后,r的地址不变,defer函数在return退出前执行,因此r会受到defer函数影响,increaseB()的返回值为1
场景五:
s1 := []int{1,2,3}
s2 := s1
s1 = append(s1, 4)
s1[0] = 100
fmt.Println("s1=", s1)
fmt.Println("s2=", s2)
//实际返回:s1= [100 2 3 4] s2= [1 2 3]
尽管golang中切片是引用类型,但因为s1发生扩容,内存地址改变,所以扩容后的s1和s2已没有引用关系。
场景六:
s1 := []int{1,2,3}
s2 := s1
s1 = append(s1[:1], s1[2:]...)
fmt.Println("s1=", s1)
fmt.Println("s2=", s2)
//实际返回:s1= [1 3] s2= [1 3 3]
对切片进行下标截取的时候,内存地址保持不变,如果头部位置不变,则切片容量也保持不变,仅是截断处指针改变,切片长度改变。 在上述场景中,s1 = append(s1[:1], s1[2:]...)相当于将原始切片[1 2 3]变成[1 ],然后追加一个元素3。由于切片容量足够,没有发生扩容,所以新追加的元素3覆盖掉了原内存地址上的2,因此s2变成了[1 3 3]
场景七:
type Subject interface {
Exam(int) string
}
type Math struct{}
func (ma *Math) Exam(score int) (result string) {
if score >= 60 {
result ="pass"
}else {
result ="not pass"
}
return
}
func main() {
var test Subject = Math{}
fmt.Println(test.Exam(60))
}
//编译时会报错
对象Math并没有实现Subject接口,实现接口的是*Math,所以正确的写法是:
var test Subject = &Math{} 或 var test = Math{}