golang编程踩坑记录

1. 函数传参 
package mainimport "fmt"func main() { slice := []int{1, 2} fmt.Printf("data:%v, len:%d, cap:%dn", slice, len(slice), cap(slice)) updateslice(slice) fmt.Printf("after data:%v, len:%d, cap:%dn", slice, len(slice), cap(slice))}func updateslice(slice []int) { slice[0] = 12slice = Append(slice, 10) fmt.Printf("inner data:%v, len:%d, cap:%dn", slice, len(slice), cap(slice))} 
执行结果为:
 
data:[1 2], len:2, cap:2after data:[12 2], len:2, cap:2 
由于切片是引用类型,所以数据修改会影响外部,实际上内外是指向同一个底层数据 。但是当对updateslice函数做append操作后,指向结果为:
 
data:[1 2], len:2, cap:2inner data:[12 2 10], len:3, cap:4after data:[12 2], len:2, cap:2 
发现内部的添加数据修改并没有影响到外部变量,此时可以看到内部的切片容量发生了扩容,之前的数据会被copy一份到新的地址,此时该扩容后的切片指向的是新的地址,函数内外切片并不是指向同一个底层数据 。
 
总结:当数据调整发生扩容时,内外切片不是指向同一个底层数据;否则,指向同一个底层数组 。
 
2. for range 
type student struct { name string ageint}func main() { m := make(map[string]*student) stus := []student{{name: "小王子", age: 18},{name: "娜扎", age: 23},{name: "大王八", age: 9000}, } // 这里出现问题 for _, stu := range stus {m[stu.name] = &stu } for k, v := range m {fmt.Println(k, "=>", v.name) }} 
对于该代码,我们的预期结果是
 
娜扎 => 娜扎大王八 => 大王八小王子 => 小王子 
但是得到的结果是:
 
小王子 => 大王八娜扎 => 大王八大王八 => 大王八 
原因是 for遍历时,变量stu是值的副本,每次遍历仅进行struct值拷贝,它指向的地址不变,所以每一次操作m[stu.name] = &stu,实际指向的都是同一个地址,遍历完成后,存储的是结构体最后一个值的拷贝 。根本原因在于for-range会使用同一块内存去接收循环中的值 。
 
应取切片中原始值的指针,修改如下:
 
【golang编程踩坑记录】for _, stu := range stus { value := stu m[stu.name] = &value} 
3.错误踩坑

  1. struct结构体中变量的名字不能和tag标签中的名字一样
  2. 避免go包互相调用中出现初始化函数init(),会发生runtime异常 。panic: runtime error: invalid memory address or nil pointer dereference
  3. Go语言中所有的传参都是值传递(传值),都是一个副本,一个拷贝 。因为拷贝的内容有时候是非引用类型(int、string、struct等),这样就在函数中无法修改原内容的数据;有的是引用类型(指针、map、slice、chan),因此可以修改原来内容 。引用类型和传引用是两个概念 。
  4. 要检查切片是否为空,请始终使用len(s) == 0来判断,而不应该使用s == nil来判断,已分配内存(使用make)的切片不等于nil,未分配内存时为nil 。切片本质上是对底层数组的封装,包含指向底层数组的地址、切片的容量、长度 。




    推荐阅读