Go中的数据结构(一) | 青训营笔记

60 阅读3分钟

基本类型的字节数

可以用Unsafe.Sizeof查看任何变量的字节大小

  • int大小跟随系统字长
  • 指针的大小也是系统字长

空结构体

空结构体大小为0 但有地址, 但所有的空结构体(独立出现, 没有被其它struct引用)都指向同一地址(zerobase, 0x8a82f8) 当被内嵌在其它struct中, 地址不是zerobase 空结构体主要是为了节约内存, 比如使用map实现hashset

// value 不占任何空间
hashSet := map[string]struct{}{}
hashSet["a"] = struct{}{}

比如用channel传输信号, 不想携带任何信息

a := make(chan struct{}, 1)

字符串

字符串本质上是个结构体

type stringStruct struct {
	str unsafe.Pointer
	len int
}

对string取Sizeof的时候, 取的是指针大小. Data指针指向底层Byte数组 len表示byte数组的长度, 编码不同, 字符的个数也不同(UTF8下, 一个字符占三个字节)

字符编码问题

所有字符都是用Unicode字符集,使用UTF-8编码

  • Unicode字符集 一种统一的字符集, 包含了绝多数文字的绝大多数字符, 14w个字符, 至少需要3字节(2^24)才能表示 英文字母排在前128个
  • UTF-8编码 Unicode的一种变长格式 128个US-ASCII字符只需一个字节编码 西方常用字符需要两个字节 其他字符3个字节, 极少4个字节

字符串遍历

自动判断多个字节是不是一个字符的不同部分(runtime下的utf8.go实现, rune就是UTF-8编码) 字符串被range遍历的时候, 被解码成rune类型的字符

s := "中国科学院"
// 这样不正确
for i := 0; i < len(s); i++ {  
	fmt.Printf("%c", s[i])  
}
// 正确方式
for _, c := range s {
	fmt.Printf("%c", c)
}

字符串切片

  1. 转为rune数组
  2. 切片
  3. 转为string
s = string([]rune(s)[:3]) // 取前三个汉字

切片

是一个结构体 切片的本质是对数组的引用

type slice struct {
  array unsafe.Pointer
  len int // 长度
  cap int // 总容量
}

切片创建

  • 根据数组创建
arr[0:3]
slice[0:3]
  • 字面量创建: 编译时插入创建数组的代码
slice := []int{1, 2, 3}
  • make: 运行时创建数组
slice := make([]int, 10)

切片扩容

  • 不扩容时, 只调整len(编译器负责)
  • 扩容时, 编译时转为调用runtime.growslice() < 1024的时候, 二倍增长, 将原数据复制过来. > 1024的时候, 每次增加25% 切片扩容时, 并发不安全, 注意切片并发需要加锁

总结

  • 字符串与切片都是对底层数组的引用
  • 字符串有UTF-8变长编码的特点
  • 切片的容量和长度不同
  • 切片追加时, 可能需要重新创建底层数组

接口

接口显式好还是隐式好?

Go隐式接口特点

  • 只要实现了接口的全部方法, 就是自动实现接口
  • 可以在不修改代码的情况下抽象出新的接口

接口的底层表示

底层使用runtime.iface表示

type iface struct{ 
	tab *itab // 接口类型, 接口装载的类型, 实现了哪些方法
	data unsafe.Pointer // 指向结构体(数据)
}

类型断言

  • 类型断言是一个使用在接口值上的操作
  • 可以将接口值转换为其它类型值(实现或者兼容接口)
  • 还可以配合switch 进行类型判断
var c Car = Truck{}
t := c.(Truck)

接受者为结构体的时候, 会自动添加一个接受者为指针的方法; 但如果用结构体指针实现了方法, 只会存在指针的. Pasted image 20230511222155.png

空接口

是eface 可以承载任何类型. 底层不是普通接口

用途

  • 作为任意类型的函数入参
  • 函数调用的时候, 会新生成一个空接口, 再传参

总结

  • Go的隐式接口更方便系统的扩展和重构
  • 接口提和指针都可以实现接口
  • 空接口值可以称在任何类型的数据