Go Slice,Map,Struct排坑
收集帖 本帖主要收集关于Slice,Map,Struct遇到的坑,不定时更新,总结
Slice
Slice底层是一个数组,在进行Slice赋值操作时,底层的数组是不改变的,只是Slice的可见长度改变了,容量也是会不变的。Slice的数据结构是包含指针,长度,容量三个值的结构体。
package main
import "fmt"
func changeSlice(arr []int){
fmt.Printf("changeSlice中传入的arr %p %v 长度:%d 容量%d\n",&arr,arr,len(arr),cap(arr))
arr[0]++
fmt.Printf("changeSlice中传入的arr change后 %p %v 长度:%d 容量%d\n",&arr,arr,len(arr),cap(arr))
arr=append(arr,0)
fmt.Printf("changeSlice中传入的arr add后 %p %v 长度:%d 容量%d\n",&arr,arr,len(arr),cap(arr))
}
func main(){
arr1:=[]int{1,2,3}
arr2:=arr1
fmt.Printf("main的arr1 %p %v 长度:%d 容量%d \n",&arr1,arr1,len(arr1),cap(arr1))
changeSlice(arr1)
fmt.Printf("main的arr1 change后 %p %v 长度:%d 容量%d \n",&arr1,arr1,len(arr1),cap(arr1))
fmt.Printf("main的arr2 %p %v 长度:%d 容量%d\n",&arr2,arr2,len(arr2),cap(arr2))
changeSlice(arr2)
fmt.Printf("main的arr2 change后 %p %v 长度:%d 容量%d\n",&arr2,arr2,len(arr2),cap(arr2))
}
输出
main的arr1 0xc000004078 [1 2 3] 长度:3 容量3
changeSlice中传入的arr 0xc0000040c0 [1 2 3] 长度:3 容量3
changeSlice中传入的arr change后 0xc0000040c0 [2 2 3] 长度:3 容量3
changeSlice中传入的arr add后 0xc0000040c0 [2 2 3 0] 长度:4 容量6
main的arr1 change后 0xc000004078 [2 2 3] 长度:3 容量3
main的arr2 0xc000004090 [2 2 3] 长度:3 容量3
changeSlice中传入的arr 0xc000004150 [2 2 3] 长度:3 容量3
changeSlice中传入的arr change后 0xc000004150 [3 2 3] 长度:3 容量3
changeSlice中传入的arr add后 0xc000004150 [3 2 3 0] 长度:4 容量6
main的arr2 change后 0xc000004090 [3 2 3] 长度:3 容量3
仔细观察上面代码,可以得出以下结论:
-
Slice是值传递,因为无论是赋值还是函数传参,获取到的地址都是不一样的,%p表示获取指针
-
虽然是值传递,这个值是指Slice指向的结构体的值,他们仍然共享同一个底层数组,修改变量时,arr改变了,arr1和arr2都会跟着改变
-
注意append,当数组元素增加大于容量时,会发生扩容(扩容是2倍扩容,>1000是1.5倍扩容),这时候arr指向的数组和arr1和arr2指向的就不是同一块底层数组了。从函数退出后,arr1和arr2的元素值、长度和容量不同就可以看出来
-
建议如果要对传入的arr进行修改,请必须返回修改后的arr!
示例如下:
package main import "fmt" func changeSlice(arr []int)[]int{ fmt.Printf("changeSlice中传入的arr %p %v 长度:%d 容量%d\n",&arr,arr,len(arr),cap(arr)) arr[0]++ fmt.Printf("changeSlice中传入的arr change后 %p %v 长度:%d 容量%d\n",&arr,arr,len(arr),cap(arr)) arr=append(arr,0) fmt.Printf("changeSlice中传入的arr add后 %p %v 长度:%d 容量%d\n",&arr,arr,len(arr),cap(arr)) return arr } func main(){ arr1:=[]int{1,2,3} arr2:=arr1 fmt.Printf("main的arr1 %p %v 长度:%d 容量%d \n",&arr1,arr1,len(arr1),cap(arr1)) arr1=changeSlice(arr1) fmt.Printf("main的arr1 change后 %p %v 长度:%d 容量%d \n",&arr1,arr1,len(arr1),cap(arr1)) fmt.Printf("main的arr2 %p %v 长度:%d 容量%d\n",&arr2,arr2,len(arr2),cap(arr2)) arr2=changeSlice(arr2) fmt.Printf("main的arr2 change后 %p %v 长度:%d 容量%d\n",&arr2,arr2,len(arr2),cap(arr2)) }
输出
main的arr1 0xc000004078 [1 2 3] 长度:3 容量3 changeSlice中传入的arr 0xc0000040c0 [1 2 3] 长度:3 容量3 changeSlice中传入的arr change后 0xc0000040c0 [2 2 3] 长度:3 容量3 changeSlice中传入的arr add后 0xc0000040c0 [2 2 3 0] 长度:4 容量6 main的arr1 change后 0xc000004078 [2 2 3 0] 长度:4 容量6 main的arr2 0xc000004090 [2 2 3] 长度:3 容量3 changeSlice中传入的arr 0xc000004150 [2 2 3] 长度:3 容量3 changeSlice中传入的arr change后 0xc000004150 [3 2 3] 长度:3 容量3 changeSlice中传入的arr add后 0xc000004150 [3 2 3 0] 长度:4 容量6 main的arr2 change后 0xc000004090 [3 2 3 0] 长度:4 容量6
for range
package main import "fmt" func main(){ arr:=[]int{1,2,3} for ind,val:=range arr{ fmt.Printf("val %p %v\n",&val,val) fmt.Printf("arr[ind] %p %v\n",&arr[ind],arr[ind]) val++ fmt.Printf("add后 val %p %v\n",&val,val) fmt.Printf("add后 arr[ind] %p %v\n",&arr[ind],arr[ind]) } }
输出
val 0xc00000a098 1 arr[ind] 0xc000012150 1 add后 val 0xc00000a098 2 add后 arr[ind] 0xc000012150 1 val 0xc00000a098 2 arr[ind] 0xc000012158 2 add后 val 0xc00000a098 3 add后 arr[ind] 0xc000012158 2 val 0xc00000a098 3 arr[ind] 0xc000012160 3 add后 val 0xc00000a098 4 add后 arr[ind] 0xc000012160 3
通过代码可以得出如下结论:
- for range得到的val的地址是同一个,只是每次更新值
- 修改val对Slice不会造成任何影响!
Map
- Map的Key必须是可比较的类型,Slice,channel,map不可比较,另外结构体是否可比较是根据结构体中的元素判断的。
package main import "fmt" func main(){ a:=make(chan int,1) b:=make(chan int,1) a<-1 b<-1 if a==b{ fmt.Println("可比较") } }
chan虽然不报错,但是上述代码并不能得出正确的比较结果,而Slice和Map一旦作比较就报错了。
- Map使用时,必须要初始化
也就是
var a map[int]int a:=make(map[]int,0)
第一个是声明,第二个是初始化
- Map的Val是结构体时,一定要使用结构体指针传值,否则无法修改结构体元素
package main import "fmt" type person struct { name string age int } func main(){ a:=make(map[string]person) a["007"]=person{"小花",15} a["008"]=person{"小刘",18} fmt.Println(a) a["008"].name="小白" }
最后一行 a["008"].name="小白"是错的
package main import "fmt" type person struct { name string age int } func main(){ a:=make(map[string]*person) a["007"]=&person{"小花",15} a["008"]=&person{"小刘",18} fmt.Println(a) a["008"].name="小白" fmt.Println(a["008"]) }
所以传结构体时,使用指针传递最好
Struct
package main import "fmt" type person struct { name string age int } func changeStruct(p person){ fmt.Println("changeStruct person %p %v",&p,p) p.age=17 fmt.Println("changeStruct person change后 %p %v",&p,p) } func main(){ person1:=person{"小刘",18} fmt.Println("main person1 %p %v",&person1,person1) changeStruct(person1) fmt.Println("main person1 change后 %p %v",&person1,person1) }
输出
main person1 %p %v &{小刘 18} {小刘 18} changeStruct person %p %v &{小刘 18} {小刘 18} changeStruct person change后 %p %v &{小刘 17} {小刘 17} main person1 change后 %p %v &{小刘 18} {小刘 18}
从以上可以得出:
- Struct是值传递,传入的只是Struct值,所以会经过值拷贝,这样也消耗很大
- 不如传入指针
package main import "fmt" type person struct { name string age int } func changeStruct(p *person){ fmt.Println("changeStruct person %p %v",&p,p) p.age=17 fmt.Println("changeStruct person change后 %p %v",&p,p) } func main(){ person1:=person{"小刘",18} fmt.Println("main person1 %p %v",&person1,person1) changeStruct(&person1) fmt.Println("main person1 change后 %p %v",&person1,person1) }
输出
main person1 %p %v &{小刘 18} {小刘 18} changeStruct person %p %v 0xc000006030 &{小刘 18} changeStruct person change后 %p %v 0xc000006030 &{小刘 17} main person1 change后 %p %v &{小刘 17} {小刘 17}
注:结构体指针和结构体调用成员的方法是一样的,都是p.name,当然对于结构体指针也支持(*p).name,对于结构体也支持&p.name。
本文作者:WinterStarHu Go语言
本文链接:https://WinterStarHu.github.io/post/go-slicemapstruct-pai-keng/
版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!
本文链接:https://WinterStarHu.github.io/post/go-slicemapstruct-pai-keng/
版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!