Welcome to Rooeye's blog

go 易混淆点梳理 (二)

golang rooeye 190℃ 0评论

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 指向的是切片结构体的地址,作图如下:

转载请注明: Jinkun 的博客 » go 易混淆点梳理 (二)

喜欢 (0)
发表我的评论
取消评论

Hi,您需要填写昵称和邮箱!

  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址