1. 基础类型的局部变量的初始化:
var a int = 1 var b = 2.0 c := 'a' fmt.Printf("%T %T %T\n", a, b, c) //int float64 int32 var a int := 1 //wrong var b := 2.0 //wrong
不写类型时能够根据值去推断出这个变量的具体类型
2. 局部变量零值为 nil 的时候初始化:
var d []int = nil //correct //var e = nil // wrong use of untyped nil //f := nil // wrong use of untyped nil
必须指明具体的 nil 的类型,因为零值为 nil 的类型有很多种,无法根据 nil 推断出变量的类型
3. 全局变量的初始化:
var a int = 1 var b = 2.0 c:='a' //wrong
全局变量不能使用 “:=” 的方式初始化,其余情况全局变量和局部变量初始化的方法相同
4. 使用下面方式生成新的切片的时候,切片的底层数组使用的就是原数组
arr := [3]int{1, 2, 3} sli := arr[:] fmt.Printf("%p\n", &arr) //0xc0000161e0 fmt.Printf("%p\n", &sli[0]) //0xc0000161e0 切片底层数组的地址 fmt.Printf("%p\n", &sli) //0xc00000a080 切片结构体的地址
5. 函数返回给调用方参数时,如果返回类型是引用类型,其底层数据结构不会发生新的拷贝
func c() []int { arr1 := []int{1, 2, 3} fmt.Printf("%p\n", &arr1[0]) //0xc0000161e0 return arr1 } func main() { arr2 := c() fmt.Printf("%p\n", &arr2[0]) //0xc0000161e0 }
func c() [3]int { arr1 := [3]int{1, 2, 3} fmt.Printf("%p\n", &arr1[0]) //0xc000016200 return arr1 } func main() { arr2 := c() fmt.Printf("%p\n", &arr2[0]) //0xc0000161e0 }
6. 将一个 byte 类型的切片转化为字符串时,底层数据结构会产生一份新的拷贝
sli := []byte{'a', 'b'} str := string(sli) sli[0] = 'c' fmt.Println(str) //ab
将一个字符串转化为 byte 类型的切片后,会给 []byte 分配一块内存,不能通过修改该切片的方式妄图修改原字符串
str := "abc" sli := []byte(str) sli[0] = 'd' fmt.Println(str) //abc
7. reflect 包下的 value.go 源文件中定义了 slice 和 string 的结构体,二者本质都是一个结构体
SliceHeader 结构体中 Data 是指向底层数组的指针,可以通过打印它的值来知晓底层数组的地址
type SliceHeader struct { Data uintptr Len int Cap int }
获取底层数组地址:
func sliceAddr(sli []int) uintptr { return (*reflect.SliceHeader)(unsafe.Pointer(&sli)).Data }
StringHeader 结构体中 Data 是指向底层数组的指针,可以通过打印它的值来知晓底层数组的地址
type StringHeader struct { Data uintptr Len int }
获取底层数组地址:
func stringAddr(s string) uintptr { return (*reflect.StringHeader)(unsafe.Pointer(&s)).Data }
一些场景需要判断切片和字符串的底层数组是否被重新分配内存,可以通过以上方法用打印底层数组地址的方法来判断
8. 引用类型的指针的是有意义的,以切片为例:
arr := [3]int{1, 2, 3} fmt.Printf("%p\n", &arr) //0xc0000161e0 sli := arr[:] fmt.Printf("%p\n", sli) //0xc0000161e0 fmt.Printf("%p\n", &sli[0]) //0xc0000161e0 fmt.Printf("%p\n", &sli) //0xc00000a080 fmt.Println(unsafe.Pointer(&sli)) //0xc00000a080 sliHeader := (*reflect.SliceHeader)(unsafe.Pointer(&sli)) fmt.Printf("0x%10x\n", sliHeader.Data) //0xc0000161e0
在打印地址的时候,sli 指向的是底层数组,&sli 指向的是切片结构体的地址,作图如下:
转载请注明:晋坤 的博客 » go 易混淆点梳理 (二)