Slice底层

数组 几乎所有计算机语言,数组的实现都是相似的:一段连续的内存。GO语言也一样,GO语言的数组底层实现就是一段连续的内存空间。 由于内存连续,CPU很容易计算索引,所以可以快速迭代数组里所有的元素。 C语言里面的Array是指向数组第一个元素的指针,GO语言的Array不像C语言里面的Array,在GO语言里,Array是一个Struct,一个数组变量就是整个数组。所以GO语言里Array是值类型,所以数组在传递的时候,传递的是原数组的拷贝。 GO语言slice底层数据结构 GO语言的Slice底层其实就是一个Struct: type slice struct{ array unsafe.Pointer len int cap int } 它的struct包含三个字段:1.指向数组的指针 2.它的长度 3.它的容量 所以说,一个slice其实是截取了某个数组的一部分而已。 由于是指针,所以它的改变会直接影响到它所指向的数组。 当然我说的是影响它所指向的数组,如果它所指向的数组被更换了,就影响更换之后的数组了。 请看这段代码: package main import ( "fmt" ) func main() { var s = [5]string{"1", "2", "3", "4", "5"} fmt.Printf("原数组s:\n %v\n", s) sli := s[0:5] newsli := append(sli, "t") sli[1] = "x" fmt.Printf("截取数组s后的sli:\n %v\n", sli) fmt.Printf("原数组s:\n %v\n", s) fmt.Printf("append sli后的newsli:\n %v\n", newsli) newsli[0] = "w" fmt.Printf("改变了newsli的第一个元素之后的newsli:\n %v\n", newsli) fmt.

关于Slice的几点总结

var和make定义方式 使用var定义 var intSlice []int var stringSlice []string //等等,不多余赘述。 使用make定义 mSlice:=make([]type,len,cap) //len是mSlice的长度,cap是mSlice的容量 //长度是已有长度,容量是这个slice的最大长度 下面这两种定义效果一样 var s1 []int s2:=make([]int,0,0) 使用make定义slice的几种方式比较 1.长度和容量都是0,即make([]type,0,0)或make([]type,0) s1:=make([]int,0) for i=0;i<10;i++ { s1=append(s1,i) fmt.Printf("切片:%v,切片地址:%p,切片长度:%v,切片容量:%v",s1,s1,len(s1),cap(s1)) } 结论 :切片长度容量是不断变化的,并且不断重新分配内存,所以效率低下。 2.长度是0,容量不是0,即make([]type,0,cap) s2:=make([]int,0,10) for i=0;i<10;i++ { s2=append(s2,i) fmt.Printf("切片:%v,切片地址:%p,切片长度:%v,切片容量:%v",s2,s2,len(s2),cap(s2)) } 结论:切片长度不断变化,容量不变,内存不重新分配,所以效率高。 3.长度容量都不是0,即make([]type,len,cap) s3:=make([]int,10,10) for i=0;i<10;i++ { s3=append(s3,i) fmt.Printf("切片:%v,切片地址:%p,切片长度:%v,切片容量:%v",s3,s3,len(s3),cap(s3)) } 结论:切片长度不断变化,容量=起初容量+长度,内存不重新分配,所以效率高,但会出现默认值(因为长度是10,所以会出现10个是0的默认值),不利于操作。 综合以上结论考虑:建议使用第二种make方式定义slice 即make([]type,0,cap)

Defer

Defer使用时的坑 先看几个例子 1. func f() (result int) { defer func() { result++ }() return 0 } 2. func f() (r int) { t := 5 defer func() { t = t + 5 }() return t } 3. func f() (r int) { defer func(r int) { r = r + 5 }(r) return 1 } 例1的正确答案不是0,例2的正确答案不是10,例3的正确答案不是6…… Why??? = =! 要使用defer时不踩坑,最重要的一点就是要明白,return xxx这一条语句并不是一条原子指令! 那啥是原子指令?? 所谓原子操作是指不会被 线程调度机制 打断的操作;这种操作一旦开始,就一直运行到结束,中间不会有任何 context switch (切换到另一个线程)。