一、nil slice & empty slice
1、nil切片与空切片底层
-  nil切片:var nilSlice [] string -  nil slice的长度len和容量cap都是0 
-  nil slice==nil 
-  nil slice的pointer是nil 
 
-  
-  空切片:emptySlice0 := make([]int,0) -  empty slice的长度是0,容量是由指向底层数组决定 
-  empty slice != nil 
-  empty slice的pointer是底层数组的地址 
 
-  
-  nil切片和空切片最大的区别在指向的数组引用地址是不一样的 
-  nil空切片引用数组指针地址为0(无指向任何实际地址) 

-  空切片的引用数组指针地址是有的,且固定为一个值,所有的空切片指向的数组引用地址都是一样的 

2、创建nil slice 和empty slice
package mainimport "fmt"func main() {var nilSlice []string // 创建一个 nil 切片emptySlice0 := make([]int, 0) // 方法1:创建一个空切片(零切片)var emptySlice1 = []string{} // 方法2:创建一个空切片fmt.Printf("\nnilSlice---> Nil:%v Len:%d Capacity:%d", nilSlice == nil, len(nilSlice), cap(nilSlice))fmt.Printf("\nemptySlice0---> nil:%v Len:%d Capacity:%d", emptySlice0 == nil, len(emptySlice0), cap(emptySlice0))fmt.Printf("\nemptySlice1---> nil:%v Len:%d Capacity:%d", emptySlice1 == nil, len(emptySlice1), cap(emptySlice1))// nil切片和空切片都可以正常 append数据nilSlice = append(nilSlice, "sss")}/*Nil:true Len:0 Capacity:0nil:false Len:0 Capacity:0nil:false Len:0 Capacity:0[sss]*/
二、类型强转产生内存拷贝
1、字符串转数组发送内存拷贝
-  字符串转成byte数组,会发生内存拷贝吗? 
-  字符串转出切片,会产生拷贝 
-  严格来说,只要是发送类型强转都会发送内存拷贝 
-  那么问题来了,频繁的内存拷贝操作听起来对性能不大友好 
-  有没有什么办法可以在字符串转出切片的时候不用发生拷贝呢? 
2、字符串转数组不内存拷贝方法
-  那么如果想要在底层转换二者,只需要吧StringHeader的地址强转成SliceHeader就行,那么go有个很强的包叫unsafe 
-  1. unsafe.Pointer(&a)方法可以得到变量a的地址。-  2. (*reflect.StringHeader)(unsafe.Pointer(&a))可以把字符串a转成底层结构的形式。
-  3. (*[]byte)(unsafe.Pointer(&ssh))可以把ssh底层结构体转成byte的切片的指针。
-  4.再通过 *转为指针指向的实际内容。
 
-  
package mainimport ("fmt""reflect""unsafe")func main() {a :="aaa"ssh := *(*reflect.StringHeader)(unsafe.Pointer(&a))b := *(*[]byte)(unsafe.Pointer(&ssh))fmt.Printf("%v---%T",b,b) // [97 97 97]---[]uint8}
三、拷贝大切片一定代价大吗?
-  SliceHeader是切片在go的底层结构。-  第一个字是指向切片 底层数组的指针,这是切片的存储空间
-  第二个字段是 切片的长度
-  第三个字段是 容量
 
-  
type SliceHeader struct {Data uintptrLen intCap int}
-  大切片跟小切片的区别无非就是 Len和Cap的值比小切片的这两个值大一些,如果发生拷贝,本质上就是拷贝上面的三个字段。
-  所以 拷贝大切片跟小切片的代价应该是一样的 
四、map不初始化使用会怎么样
-  空map和nil map结果是一样的,都为map[]。 
-  所以,这个时候别断定map是空还是nil,而应该通过map == nil来判断。 
package mainfunc main() {var m1 map[string]string // 创建一个 nil mapprintln("m1为nil: ", m1==nil)// 报错 => panic: assignment to entry in nil map//m1["name"] = "tom"var m2 = make(map[string]string) // 创建一个空mapm2["name"] = "jack" // 空map可以正常println("m2为nil: ", m2==nil)}
五、map会遍历删除安全吗?
-  map 并不是一个线程安全的数据结构。 
-  同时读写一个 map 是未定义的行为,如果被检测到,会直接 panic。 
-  上面说的是发生在多个协程同时读写同一个 map 的情况下。 
-  如果在同一个协程内边遍历边删除,并不会检测到同时读写,理论上是可以这样做的。 
-  sync.Map可以解决多线程读写map问题 -  一般而言,这可以通过读写锁来解决: sync.RWMutex。
-  读之前调用 RLock()函数,读完之后调用RUnlock()函数解锁;
-  写之前调用 Lock()函数,写完之后,调用Unlock()解锁。
-  另外, sync.Map是线程安全的 map,也可以使用
 
-  
六、for循环append坑
1、坑1:添加元素变覆盖
-  不会死循环, for range其实是golang的语法糖,在循环开始前会获取切片的长度len(切片),然后再执行len(切片)次数的循环。
package mainimport "fmt"func main() {s := []int{1,2,3,4,5}for _, v:=range s {s =append(s, v)fmt.Printf("len(s)=%v\n",len(s))}}/*len(s)=6len(s)=7len(s)=8len(s)=9len(s)=10*/
2、坑2:值全部一样
-  每次循转中num的值是正常的,但是由append构造的res中,全是nums的最后一个值。 
-  最终总结出原因是在for range语句中,创建了变量num且只被创建了一次。 
-  即num有自己的空间内存且地址在for循环过程中不变 
-  循环过程中每次将nums中对应的值和num进行值传递 
package mainimport "fmt"func main() {var nums = []int{1, 2, 3, 4, 5}var res []*intfor _, num := range nums {res = append(res, &num)//fmt.Println("num:", num)}for _, r := range res {fmt.Println("res:", *r)}}/*res: 5res: 5res: 5res: 5res: 5*/
3、解决方法
-  方法1-  不使用for range的形式,直接用索引来对nums取值 
 
-  
package mainimport "fmt"func main() {var nums = []int{1, 2, 3, 4, 5}var res []*intfor i := 0; i < len(nums); i++ {res = append(res, &nums[i])}fmt.Println("res:", res)for _, r := range res {fmt.Println("res:", *r)}}
-  方法2-  在for循环中每次再定义一个新的变量num_temp,将num的值传给num_temp,之后append该变量即可。 
 
-  
package mainimport "fmt"func main() {var nums = []int{1, 2, 3, 4, 5}var res []*intfor _, num := range nums {numTemp := num // 创建一个新的临时变量res = append(res, &numTemp)}for _, r := range res {fmt.Println("res:", *r)}}/*res: 1res: 2res: 3res: 4res: 5*/



















