文章目录

  • 内置类型
    • 值类型
      • 内置常量
    • 引用类型
      • 内置函数
      • 内置接口
  • 数据类型
    • 基本类型
      • 整型
      • 浮点型
      • 布尔型
      • 字符和字符串
        • 字符
        • 字符串
      • 类型转换
    • 派生类型
      • 数组 array
      • 多维数组
      • 切片 slice
      • 哈希表 map
      • 指针类型 pointer
        • 声明
        • 取指针
        • 内存
  • 类型判断
    • comma-ok断言
    • type - switch

内置类型

定义在builtin/builtin.go

值类型

    boolint  int8, int16, int32(rune), int64uint uint8(byte), uint16, uint32, uint64float32, float64stringcomplex64, complex128array  //固定长度的数组struct //结构体

内置常量

   true   //boolfalse  iota   //int 初始0nil   //nil类型值

引用类型

    slice    //切片map      //哈希映射chan     //通道// 特殊  零值 nilfunction  //函数pointer   //指针  interface //接口

内置函数

    append          // 用来追加元素到数组、slice中,返回修改后的数组、sliceclose           // 主要用来关闭channeldelete          // 从map中删除key对应的valuepanic           // 停止常规的goroutine  (panic和recover:用来做错误处理)recover         // 允许程序定义goroutine的panic动作real            // 返回complex的实部   (complex、real imag:用于创建和操作复数)imag            // 返回complex的虚部make            // 用来分配内存,返回Type本身(只能应用于slice, map, channel)new             // 用来分配内存,主要用来分配值类型,比如int、struct。返回指向Type的指针cap             // capacity是容量的意思,用于返回某个类型的最大容量(只能用于切片和 map)copy            // 用于复制和连接slice,返回复制的数目len             // 来求长度,比如string、array、slice、map、channel ,返回长度print、println  // 底层打印函数,在部署环境中建议使用 fmt 包

内置接口

//error接口
type error interface {Error() string
}

数据类型

基本类型

整型

类型 描述
uint8 无符号 8 位整型 (0 到 255)
uint16 无符号 16 位整型 (0 到 65535)
uint32 无符号 32 位整型 (0 到 4294967295)
uint64 无符号 64 位整型 (0 到 18446744073709551615)
int8 有符号 8 位整型 (-128 到 127)
int16 有符号 16 位整型 (-32768 到 32767)
int32 有符号 32 位整型 (-2147483648 到 2147483647)
int64 有符号 64 位整型 (-9223372036854775808 到 9223372036854775807)
byte uint8,存储字符时选用
rune int32,表示Unicode码
uint 32 或 64 位
int 与 uint 一样大小,整型没有声明具体类型时,默认的类型
uintptr 无符号整型,用于存放一个指针

浮点型

类型 描述
float32 单精度,IEEE-754 32位浮点型数,有效bit位23个 浮点最精度小数点后15位
float64 双精度,IEEE-754 64位浮点型数,默认的类型 浮点最精度小数点后15位
complex64 32 位实数和虚数
complex128 64 位实数和虚数

布尔型

  1. 常量true false,默认fasle
  2. 不可与整型计算,不可以强转
package mainimport ("fmt""math""unsafe"
)
func main() {//-------------类型和所占字节----------------num := 2fmt.Printf("num is type %T, size %d\n",num,unsafe.Sizeof(num))//------------超范围,循环显示----------------var n1 int8 = 1var n3 = n1 + 127fmt.Println("n3:",n3)//-----------不同类型,不能操作-------------// var n4  = num + n1 invalid operation//---------------浮点型----------------------var f float64 = 1f1 := 1.23f2 := .23f3 := 1.fmt.Println("f:",f,"f1:",f1,"f2:",f2,"f3:",f3)//-------------科学计数法-----------------f4 := 1.23E2f5 := 123E-2fmt.Println("f4:",f4,"f5:",f5)//---------------保留位数----------------f6 := math.Pifmt.Printf("%.3f\n",f6)//--------------四舍六入五看尾---------------fmt.Printf("3.1249    =>  %0.2f(四舍)\n", 3.1249)fmt.Printf("3.12671  =>  %0.2f(六入)\n", 3.12671)fmt.Printf("3.1351  =>  %0.2f(五后非零就进一)\n", 3.1351)fmt.Printf("3.12501 =>  %0.2f(五后非零就进一)\n", 3.12501)fmt.Printf("3.1250 =>  %0.2f(五后为零看奇偶,五前为偶应舍去)\n", 3.1250)fmt.Printf("9.8350   =>  %0.2f(五后为零看奇偶,五前为奇要进一)\n", 9.8350)fmt.Printf("3.2250   =>  %0.2f(五后为零看奇偶,五前为偶应舍去???)\n", 3.2250)fmt.Printf("9.7350   =>  %0.2f(五后为零看奇偶,五前为奇要进一???)\n", 9.7350)// 布尔b := false// n3 = n1 + b invaliad operationfmt.Printf("b的类型:%T,b的值:%v,b的大小:%d",b,b,unsafe.Sizeof(b))}

字符和字符串

字符

  1. 没有字符类型。Ascii码使用byte,其他适合使用rune(UTF-8),使用’'包裹

    a. 字母一个字节
    b. 汉字三个字节

  2. 转义字符

    转义 含义
    \r 回车符(返回行首)
    \n 换行符(直接跳到下一行的同列位置)
    \t 制表符
    单引号
    " 双引号
    \ 反斜杠

字符串

  1. 使用""包裹或者``包裹
  2. 如果只包含ascii码,为byte数组
  3. 如果包含中文,为rune数组。rune由1个或多个byte组成

转译

// 双引号支持转译字符
str3 := "我要换行\n换好啦:)\n"
// 反引号不支持转译字符
str4 := `我想换行\n换不了:(\n`
// 反引号支持多行
str5 := `line1
line2
`

切片

fmt.Println("1234567"[:3],"1234567"[3:]) // "123"  "4567"
fmt.Println("我是字符串"[:3],"我是字符串"[3:]) // "我"  "是字符串"

拼接

"a"+"b"
+"c" ❌"a"+"b"+
"c"  ☑️

遍历

a:= "我是字符串"
//读取byte
for i:=0;i<len(a);i=i+3{fmt.Print(a[i:i+3])
}//读取rune 字符
for _,s := range "a"{fmt.Printf("%c",s)
}

长度

a:= "我是字符串"
println(len(a))  // 15 byte
println(len([]rune(a)))  // 5 rune

转换

a:= "我是字符串"
b:=[]byte(a)
c:=[]rune(a)
d:=string(b)
e:=string(c)

工具

//常见工具包
strings
bytes
unicode

官网doc

中文

类型转换

参考博客

  1. go不支持隐式转换

  2. 转换表达式

    // 表达式 T(v) 将值 v 转换为类型 T
    // type_name 类型
    // expression 表达式
    type_name(expression)
    
原类型 目标类型 方法 备注
整型、浮点型 整型、浮点型 T(v) 大数转小数超范围,溢出;
浮点型转整型,小数丢失;
高精度转低精度,精度丢失
任何类型 string fmt.Sprintf() 推荐
bool strconv.FormatBool(b bool) string 根据b的值返回"true"或"false"
int64 strconv.FormatInt(i uint64, base int) string base 必须在2到36之间,结果中会使用小写字母’a’到’z’表示大于10的数字
uint64 strconv.FormatUint(i uint64, base int) string 同上且为无符号
float64/32 strconv.FormatFloat(f float64, fmt byte, prec, bitSize int) string fmt表示格式:
‘f’(-ddd.dddd)
‘b’(-ddddp±ddd,指数为二进制)
‘e’(-d.dddde±dd,十进制指数)
‘E’(-d.ddddE±dd,十进制指数)
‘g’(指数很大时用’e’格式,否则’f’格式)
‘G’(指数很大时用’E’格式,否则’f’格式)。
prec控制精度(排除指数部分):
对’f’、‘e’、‘E’,它表示小数点后的数字个数;
对’g’、‘G’,它控制总的数字个数。
如果prec 为-1,则代表使用最少数量的、但又必需的数字来表示f。
bitSize表示f的来源类型(32:float32、64:float64),会据此进行舍入。
int strconv.Itoa() 相当于strcov.ParseInt(s string, 10,0) (i int64, err error)
string strconv.ParseBool(str string) (value bool, err error) 返回字符串表示的bool值。它接受1、0、t、f、T、F、true、false、True、False、TRUE、FALSE;否则返回错误。
float64 strcov.ParseFloat(s string, bitSize int) (f float64, err error) bitSize指定了期望的接收类型,
32是float32(返回值可以不改变精确值的赋值给float32),
64是float64;
返回值err是*NumErr类型的,语法有误的,err.Error=ErrSyntax;
结果超出表示范围的,返回值f为±Inf,err.Error= ErrRange。
int64 strcov.ParseInt(s string, base int, bitSize int) (i int64, err error) 返回字符串表示的整数值,接受正负号。

base指定进制(2到36),如果base为0,则会从字符串前置判断,"0x"是16进制,"0"是8进制,否则是10进制;

bitSize指定结果必须能无溢出赋值的整数类型,0、8、16、32、64 分别代表 int、int8、int16、int32、int64;返回的err是*NumErr类型的,如果语法有误,err.Error = ErrSyntax;如果结果超出类型范围err.Error = ErrRange。

uint64 ParseFloat(s string, bitSize int) (f float64, err error) 同上,无符号版本

派生类型

数组 array

  • 值类型,声明时零值填。

  • 固定长度,编译期决定。

    • 类型不一致数组不一致
    • 长度不一致数组不一致。
    • 0~len-1下标访问 ,越界panic
  • 占用内存连续

// cmd/compile/internal/types/type.go
// array结构
type Array struct {Elem  *Type // 类型指针Bound int64 // 数组长度
}// cmd/compile/internal/types/type.go
// 创建array
func NewArray(elem *Type, bound int64) *Type {if bound < 0 {Fatalf("NewArray: invalid bound %v", bound)}t := New(TARRAY)t.Extra = &Array{Elem: elem, Bound: bound}t.SetNotInHeap(elem.NotInHeap())return t
}

//--------------声明---------------
//var variable_name [SIZE]variable_type
var arr [3]int//--------------初始化---------------
//var variable_name [SIZE]variable_type = [SIZE]variable_type{elem...}
//variable_name:= [SIZE]variable_type{elem...}
//variable_name:= [...]variable_type{elem...}
//variable_name:= [SIZE]variable_type{index:elem ...}
var arr1 [3]int = [3]int{1,2,3}  //无类型推导
var arr2 = [3]int{1:4,0:5,2:6}   //类型推导
arr3 := [...]int{7,8,9}          //长度推导
arr4 := [5]int{1:3,3:5}          //使用索引初始化
arr5 := [...] struct{            name stringage  int
}{{"user1",1},{"user2",2},
}//--------------遍历---------------
n1 := len(arr1)
for i:=0;i<n1;i++{              // forfmt.Println(i,arr1[i])
}for index,n := range arr3{     // rangefmt.Println(index,n)
}

多维数组

//--------------声明---------------
//var variable_name [ROW][COL] variable_type
var arr7 [2][3]int//--------------初始化---------------
//var variable_name [ROW][COL]variable_type = [...][COL]variable_typevariable_type{{elem...}...}
//variable_name:= [ROW][COL]variable_type{{elem...}...}
var arr8 = [...][2]int{{1,2},{2,3}}
arr9 := [1][2]int{{5,6}}//--------------遍历---------------
for row:=0;row<len(arr8);row++{for col:=0;col<len(arr8[0]);col++{fmt.Println(row,col,arr8[row][col])}}for row,rows := range arr8 {for col,val:= range rows{fmt.Println(row,col,arr8[row][col],val)}
}

切片 slice

  • 引用类型
  • 长度可以变化,容量随长度变化
  • 底层是数组
// cmd/compile/internal/types/type.go
// 编译时slice 结构体
type Slice struct {Elem *Type // element type
}// reflect/value.go
// 运行时slcie 结构体
type SliceHeader struct {Data uintptr   //指向底层数组指针Len  int       //切片长度Cap  int       //切片容量
}// cmd/compile/internal/types/type.go
// 创建切片类型 返回切片指针
func NewSlice(elem *Type) *Type {if t := elem.Cache.slice; t != nil {if t.Elem() != elem {Fatalf("elem mismatch")}return t}t := New(TSLICE)t.Extra = Slice{Elem: elem}elem.Cache.slice = treturn t
}// runtime/slice.go
// 初始化切片时,如果切片内存逃逸或者太大会调用此方法分配内存
func makeslice(et *_type, len, cap int) unsafe.Pointer {mem, overflow := math.MulUintptr(et.size, uintptr(cap)) //内存空间=切片中元素大小×切片容量if overflow || mem > maxAlloc || len < 0 || len > cap {mem, overflow := math.MulUintptr(et.size, uintptr(len))if overflow || mem > maxAlloc || len < 0 {panicmakeslicelen()}panicmakeslicecap()}return mallocgc(mem, et, true)
}

//--------------声明---------------
//var slicename []type
var slice []int  // slice = nil len=0  cap=0//--------------初始化---------------
// func make(Type, size ...IntegerType[,capacity]) Type
// capacity必须大雨size
// size 为切片初始长度
// capactity 为切片容量
// var slicename []type = make([]type,len[,cap])
// slicename := []type{elem...}
// slicename := slice[:end[:max]] start<=index<end  max<=end<=len(slice)  len=end-start cap=max-start
// slicename := slice[start:end[:max]] start<=index<end   max<=end<=len(slice)  len=end-start cap=max-start
// slicename := slice[start:[:max]] start<=index<len(slice) max<=end<=len(slice) len=end-start cap=max-start
slice1 := make([]int,1,2,3,4)  // len=3  cap=4
slice2 := []int{6,7}
slice3 := slice1[1:] //--------------遍历---------------
for i:=0;i<len(slice3);i++{fmt.Print(slice3[i]," ")
}for _,v := range slice3{fmt.Print(v," ")
}//--------------len/cap---------------
//获取切片长度
//获取切片容量
len(slice1) //3
cap(slice1) //4//--------------追加---------------
// func append(slice []Type, elems ...Type) []Type
// slice 被添加的切片
// elems 可变元素
// []Type 新切片可能扩容
slice1=append(slice1,5)  // append 追加当个元素
slice1=append(slice1,slice2...) // append 追加切片//--------------拷贝---------------
// copy(taget,soure) 长度以len(soure)为准
s1 := []int{1, 2, 3, 4, 5}
fmt.Println( s1) // [1 2 3 4 5]
s2 := make([]int, 10)
fmt.Println(s2) // [0 0 0 0 0 0 0 0 0 0]
copy(s2, s1)
fmt.Println(s1) //[1 2 3 4 5]
fmt.Println(s2) //[1 2 3 4 5 0 0 0 0 0]

拷贝

//func copy(dst, src []Type) int
//内置函数copy编译时被改写如下
//cmd/compile/internal/gc/walk.go
/*
// Lower copy(a, b) to a memmove call or a runtime call.
//
// init {
//   n := len(a)
//   if n > len(b) { n = len(b) }
//   if a.ptr != b.ptr { memmove(a.ptr, b.ptr, n*sizeof(elem(a))) }
// }
// n;
//
// Also works if b is a string.
//
*/
func copyany(n *Node, init *Nodes, runtimecall bool) *Node {//如果有指针调用 runtime/mbarrier.go typedslicecopy 拷贝if n.Left.Type.Elem().HasPointers() {...fn := writebarrierfn("typedslicecopy", n.Left.Type.Elem(), n.Right.Type.Elem())...return mkcall1(fn, n.Type, init, typename(n.Left.Type.Elem()), ptrL, lenL, ptrR, lenR)}//如果时运行时调用 runtime/slice.go typedslicecopy 拷贝if runtimecall {...fn := syslook("slicecopy")fn = substArgTypes(fn, ptrL.Type.Elem(), ptrR.Type.Elem())return mkcall1(fn, n.Type, init, ptrL, lenL, ptrR, lenR, nodintconst(n.Left.Type.Elem().Width))}....
}// runtime/slice.go
// 无指针类型切片或者string copy到另一个切片
func slicecopy(toPtr unsafe.Pointer, toLen int, fromPtr unsafe.Pointer, fromLen int, width uintptr) int {if fromLen == 0 || toLen == 0 {return 0}n := fromLenif toLen < n {n = toLen}if width == 0 {return n}size := uintptr(n) * width//其他代码....if size == 1 { *(*byte)(toPtr) = *(*byte)(fromPtr) // known to be a byte pointer} else {memmove(toPtr, fromPtr, size)}return n
}//runtime/mbarrier.go
//go:nosplit 有指针类型切片copy到另一个切片
func typedslicecopy(typ *_type, dstPtr unsafe.Pointer, dstLen int, srcPtr unsafe.Pointer, srcLen int) int {n := dstLenif n > srcLen {n = srcLen}if n == 0 {return 0}// 其他代码...size := uintptr(n) * typ.sizeif writeBarrier.needed {pwsize := size - typ.size + typ.ptrdatabulkBarrierPreWrite(uintptr(dstPtr), uintptr(srcPtr), pwsize)}memmove(dstPtr, srcPtr, size)return n
}

追加与扩容

//func append(slice []Type, elems ...Type) []Type
//内置函数append编译时被改写
//cmd/compile/internal/gc/ssa.go
func (s *state) append(n *Node, inplace bool) *ssa.Value {// If inplace is false, 不需要赋值变量 "append(s, e1, e2, e3)"://// ptr, len, cap := s// newlen := len + 3// if newlen > cap {//     ptr, len, cap = growslice(s, newlen)    // 调用切片扩容//     newlen = len + 3 // recalculate to avoid a spill// }// // with write barriers, if needed:// *(ptr+len) = e1// *(ptr+len+1) = e2// *(ptr+len+2) = e3// return makeslice(ptr, newlen, cap)////// If inplace is true, 需要赋值变量 "s = append(s, e1, e2, e3)"://// a := &s// ptr, len, cap := s// newlen := len + 3// if uint(newlen) > uint(cap) {//    newptr, len, newcap = growslice(ptr, len, cap, newlen)  // 调用切片扩容//    vardef(a)       // if necessary, advise liveness we are writing a new a//    *a.cap = newcap // write before ptr to avoid a spill//    *a.ptr = newptr // with write barrier// }// newlen = len + 3 // recalculate to avoid a spill// *a.len = newlen// // with write barriers, if needed:// *(ptr+len) = e1// *(ptr+len+1) = e2// *(ptr+len+2) = e3...
}// runtime/slice.go
// 扩容核心代码
func growslice(et *_type, old slice, cap int) slice {//1. 大致确定扩容容量newcap := old.capdoublecap := newcap + newcapif cap > doublecap {newcap = cap              //a.如果期望容量大于当前容量的两倍就会使用期望容量} else {if old.len < 1024 {newcap = doublecap      //b.当前切片的长度小于 1024 就会将容量翻倍} else {for 0 < newcap && newcap < cap {newcap += newcap / 4  //c.当前切片的长度大于 1024 就会每次增加 25% 的容量,直到新容量大于期望容量}if newcap <= 0 {newcap = cap}}}   //2. 根据元素大小做内存对齐var overflow boolvar lenmem, newlenmem, capmem uintptrswitch {case et.size == 1:           // 1lenmem = uintptr(old.len)newlenmem = uintptr(cap)capmem = roundupsize(uintptr(newcap))overflow = uintptr(newcap) > maxAllocnewcap = int(capmem)case et.size == sys.PtrSize: // 8lenmem = uintptr(old.len) * sys.PtrSizenewlenmem = uintptr(cap) * sys.PtrSizecapmem = roundupsize(uintptr(newcap) * sys.PtrSize)overflow = uintptr(newcap) > maxAlloc/sys.PtrSizenewcap = int(capmem / sys.PtrSize)case isPowerOfTwo(et.size):  // 2的倍数...default:...}//3. 内存申请,底层数组拷贝var p unsafe.Pointerif et.ptrdata == 0 {  //指针类型p = mallocgc(capmem, nil, false)memclrNoHeapPointers(add(p, newlenmem), capmem-newlenmem) //清空超出当前长度的内存} else {              //非指针类型p = mallocgc(capmem, et, true)if lenmem > 0 && writeBarrier.enabled {bulkBarrierPreWriteSrcOnly(uintptr(p), uintptr(old.array), lenmem-et.size+et.ptrdata)}}memmove(p, old.array, lenmem) //原数组拷贝到新内存中return slice{p, old.len, newcap}
}

https://www.jianshu.com/p/54be5b08a21c
https://blog.csdn.net/qq_35423190/article/details/112803978
https://www.topgoer.cn/docs/golang/chapter03-11

哈希表 map

  • 引用类型
  • Map 是 hash 表来实现的,一种无序的键值对的集合。
  • key 类似于索引,指向数据的value,**key必须可以使用==运算符来比较,不能重复。**int、布尔、string、或包含前面的struct、数组等
  • map自动扩容
//--------------声明---------------
// var mapname map[key_type]value_type
var m map[string]string//--------------初始化---------------
// var mapname map[key_type]value_type = make(map[key_type]value_type,[size])
// mapname:=map[key_type]value_type{key:value...}
// func make(Type, size IntegerType) Type
// Type  map[key_type]value_type
// size  map容量预估
var m1 = make(map[string]string,5)
m2 := map[string]string{"野王":"赵云"}
m3 := make(map[string]string)//--------------查找---------------
// m[key]返回值和是否存在(bool类型)
val,flag := m2["野王"]
if flag{fmt.Println("野王:",val)
}//--------------修改---------------
//map[key] = value 覆盖方式
m1["上官"] = "言为心声"
m1["婉儿"] = "字为心画"
m2["野王"] = "老虎"
m2["上单"] = "吕布"//--------------删除---------------
//func delete(m map[Type]Type1, key Type)
delete(m1,"上官")//--------------清空---------------
// make其他当前
m1 = make(map[string]string,0)//--------------遍历---------------
// for range
// map无序
for key,value := range m2{fmt.Println(key,value)
}// 遍历keys
for key := range m2{fmt.Println(key)
}

源码分析

const(// flagsiterator     = 1 // there may be an iterator using bucketsoldIterator  = 2 // there may be an iterator using oldbucketshashWriting  = 4 // a goroutine is writing to the mapsameSizeGrow = 8 // the current map growth is to a new map of the same size
)// 哈希表结构体
// runtime/map.go
type hmap struct {count     int // 哈希表元素个数。len()可获取flags     uint8  // 扩容标志位B         uint8  // len(buckets) == 2^Bnoverflow uint16 // 溢出的buckets数hash0     uint32 // 哈希种子 创建哈希表时确定 提供随机性buckets    unsafe.Pointer // buckets数组指针oldbuckets unsafe.Pointer // 哈希在扩容时用于复制的buckets数组指针,它的大小是当前buckets数组的一半nevacuate  uintptr        // 搬迁进度(已经搬迁的buckets数量)extra *mapextra
}type mapextra struct {overflow    *[]*bmap  //存储hamp.buckets 所有溢出桶oldoverflow *[]*bmap  //存储hamp.oldbuckets 所有溢出桶nextOverflow *bmap    //指向下一个overflow的bucket
}// 哈希表结构体中的 bucket
// runtime/map.go
type bmap struct {// tophash[0] < minTopHash为存储 bucket evacuation 状态.tophash [bucketCnt]uint8   // 因为不支持动态类型,编译时推导内存大小// cmd/compile/internal/gc/refect// func bmap(t *types.Type) *types.Type {} // topbits  [8]uint8      //哈希高8位  减少全等对比提高效率// keys     [8]keytype    //哈希表健// elems    [8]elemtype   //哈希表值// overflow uintptr       //溢出桶指针
}// 编译时生成hmap代码
// cmd/compile/internal/gc/refect
func hmap(t *types.Type) *types.Type {//其他代码...// build a struct:// type hmap struct {//    count      int//    flags      uint8//    B          uint8//    noverflow  uint16//    hash0      uint32//    buckets    *bmap//    oldbuckets *bmap//    nevacuate  uintptr//    extra      unsafe.Pointer // *mapextra// }// must match runtime/map.go:hmap.fields := []*types.Field{makefield("count", types.Types[TINT]),makefield("flags", types.Types[TUINT8]),makefield("B", types.Types[TUINT8]),makefield("noverflow", types.Types[TUINT16]),makefield("hash0", types.Types[TUINT32]), // Used in walk.go for OMAKEMAP.makefield("buckets", types.NewPtr(bmap)), // Used in walk.go for OMAKEMAP.makefield("oldbuckets", types.NewPtr(bmap)),makefield("nevacuate", types.Types[TUINTPTR]),makefield("extra", types.Types[TUNSAFEPTR]),}//其他代码...return hmap
}const(bucketCntBits = 3bucketCnt     = 1 << bucketCntBitsminTopHash     = 5 // minimum tophash for a normal filled cell.
)// 编译时生成bmap代码
// cmd/compile/internal/gc/refect
func bmap(t *types.Type) *types.Type {arr := types.NewArray(types.Types[TUINT8], BUCKETSIZE)field = append(field, makefield("topbits", arr))arr = types.NewArray(keytype, BUCKETSIZE)arr.SetNoalg(true)keys := makefield("keys", arr)field = append(field, keys)arr = types.NewArray(elemtype, BUCKETSIZE)arr.SetNoalg(true)elems := makefield("elems", arr)field = append(field, elems)otyp := types.NewPtr(bucket)if !elemtype.HasPointers() && !keytype.HasPointers() {otyp = types.Types[TUINTPTR]}overflow := makefield("overflow", otyp)field = append(field, overflow)bucket.SetNoalg(true)bucket.SetFields(field[:])//其他代码...return bucket
}// 初始化编译时
// cmd/compile/internal/gc/sinit.go
func maplit(n *Node, m *Node, init *Nodes) {// make the map vara := nod(OMAKE, nil, nil)a.Esc = n.Esca.List.Set2(typenod(n.Type), nodintconst(int64(n.List.Len())))litas(m, a, init)entries := n.List.Slice()// 其他代码...// 元素超过25个使用for循环加入hash表if len(entries) > 25 {tk := types.NewArray(n.Type.Key(), int64(len(entries)))te := types.NewArray(n.Type.Elem(), int64(len(entries)))// 其他代码...// make and initialize static arraysvstatk := readonlystaticname(tk)vstate := readonlystaticname(te)// 其他代码...// loop adding structure elements to map// for i = 0; i < len(vstatk); i++ {//  map[vstatk[i]] = vstate[i]// }// 其他代码...return}// 元素不超过25转换为所有键值对一次性加入哈希表// Build list of var[c] = expr.// Use temporaries so that mapassign1 can have addressable key, elem.// 其他代码...
}//运行时初始化
// runtime/map.go
func makemap(t *maptype, hint int, h *hmap) *hmap {mem, overflow := math.MulUintptr(uintptr(hint), t.bucket.size)if overflow || mem > maxAlloc {hint = 0}// initialize Hmapif h == nil {h = new(hmap)}h.hash0 = fastrand() // 获取hash种子// hint为计算的最小需要同的数量// Find the size parameter B which will hold the requested # of elements.// For hint < 0 overLoadFactor returns false since hint < bucketCnt.B := uint8(0)for overLoadFactor(hint, B) {B++}h.B = B// 初始化桶if h.B != 0 {var nextOverflow *bmaph.buckets, nextOverflow = makeBucketArray(t, h.B, nil)if nextOverflow != nil {h.extra = new(mapextra)h.extra.nextOverflow = nextOverflow}}return h
}// runtime/map.go
// makeBucketArray 根据传入的B创建桶数量并分配连续内存存储数据.
func makeBucketArray(t *maptype, b uint8, dirtyalloc unsafe.Pointer) (buckets unsafe.Pointer, nextOverflow *bmap) {base := bucketShift(b)nbuckets := base// 桶数量小于2^4,溢出桶使用可能较小,避免溢出桶创建// 桶数量大于2^4,额外创建2^(b-4)个溢出桶if b >= 4 {nbuckets += bucketShift(b - 4)sz := t.bucket.size * nbucketsup := roundupsize(sz)if up != sz {nbuckets = up / t.bucket.size}}if dirtyalloc == nil {buckets = newarray(t.bucket, int(nbuckets))} else {// dirtyalloc was previously generated by// the above newarray(t.bucket, int(nbuckets))// but may not be empty.buckets = dirtyallocsize := t.bucket.size * nbucketsif t.bucket.ptrdata != 0 {memclrHasPointers(buckets, size)} else {memclrNoHeapPointers(buckets, size)}}if base != nbuckets {// We preallocated some overflow buckets.// To keep the overhead of tracking these overflow buckets to a minimum,// we use the convention that if a preallocated overflow bucket's overflow// pointer is nil, then there are more available by bumping the pointer.// We need a safe non-nil pointer for the last overflow bucket; just use buckets.nextOverflow = (*bmap)(add(buckets, base*uintptr(t.bucketsize)))last := (*bmap)(add(buckets, (nbuckets-1)*uintptr(t.bucketsize)))last.setoverflow(t, (*bmap)(buckets))}return buckets, nextOverflow
}// 访问
/* 编译时会进行如下中代码生成
v     := hash[key] // => v     := *mapaccess1(maptype, hash, &key)
v, ok := hash[key] // => v, ok := mapaccess2(maptype, hash, &key)
*/
// runtime/map.go
func mapaccess1(t *maptype, h *hmap, key unsafe.Pointer) unsafe.Pointer {if raceenabled && h != nil {callerpc := getcallerpc()pc := funcPC(mapaccess1)racereadpc(unsafe.Pointer(h), callerpc, pc)raceReadObjectPC(t.key, key, callerpc, pc)}if msanenabled && h != nil {msanread(key, t.key.size)}if h == nil || h.count == 0 {if t.hashMightPanic() {t.hasher(key, 0) // see issue 23734}return unsafe.Pointer(&zeroVal[0])}if h.flags&hashWriting != 0 {throw("concurrent map read and map write")}// 1. 获取当前key hashhash := t.hasher(key, uintptr(h.hash0))m := bucketMask(h.B)// 2. 获取桶序号  hash取后B位表示在那个桶b := (*bmap)(add(h.buckets, (hash&m)*uintptr(t.bucketsize)))if c := h.oldbuckets; c != nil {if !h.sameSizeGrow() {// There used to be half as many buckets; mask down one more power of two.m >>= 1}oldb := (*bmap)(add(c, (hash&m)*uintptr(t.bucketsize)))if !evacuated(oldb) {b = oldb}}// 获取高8位hashtop := tophash(hash)// 3.循环依次查找正常桶和溢出桶数据
bucketloop:for ; b != nil; b = b.overflow(t) {for i := uintptr(0); i < bucketCnt; i++ {// 高8位比较(低概率)if b.tophash[i] != top {if b.tophash[i] == emptyRest {break bucketloop}continue}// 获取对应kk := add(unsafe.Pointer(b), dataOffset+i*uintptr(t.keysize))if t.indirectkey() {k = *((*unsafe.Pointer)(k))}// 比较k是否符合(低概率)if t.key.equal(key, k) {e := add(unsafe.Pointer(b), dataOffset+bucketCnt*uintptr(t.keysize)+i*uintptr(t.elemsize))if t.indirectelem() {e = *((*unsafe.Pointer)(e))}return e}}}return unsafe.Pointer(&zeroVal[0])
}// 写入与扩容
// runtime/map.go
// Like mapaccess, but allocates a slot for the key if it is not present in the map.
func mapassign(t *maptype, h *hmap, key unsafe.Pointer) unsafe.Pointer {if h == nil {panic(plainError("assignment to entry in nil map"))}if raceenabled {callerpc := getcallerpc()pc := funcPC(mapassign)racewritepc(unsafe.Pointer(h), callerpc, pc)raceReadObjectPC(t.key, key, callerpc, pc)}if msanenabled {msanread(key, t.key.size)}if h.flags&hashWriting != 0 {throw("concurrent map writes")}// 1. 获取当前key hashhash := t.hasher(key, uintptr(h.hash0))// Set hashWriting after calling t.hasher, since t.hasher may panic,// in which case we have not actually done a write.h.flags ^= hashWritingif h.buckets == nil {h.buckets = newobject(t.bucket) // newarray(t.bucket, 1)}again:bucket := hash & bucketMask(h.B)// 如果在迁移if h.growing() {growWork(t, h, bucket)}// 2. 获取桶序号 获取桶序号  hash取后B位表示在那个桶b := (*bmap)(add(h.buckets, bucket*uintptr(t.bucketsize)))// 获取高8位hashtop := tophash(hash)var inserti *uint8           //目标元素在桶中的索引var insertk unsafe.Pointer   //目标元素健var elem unsafe.Pointer      //目标元素值
bucketloop:for {for i := uintptr(0); i < bucketCnt; i++ {// 匹配tophashif b.tophash[i] != top {if isEmpty(b.tophash[i]) && inserti == nil {inserti = &b.tophash[i]insertk = add(unsafe.Pointer(b), dataOffset+i*uintptr(t.keysize))elem = add(unsafe.Pointer(b), dataOffset+bucketCnt*uintptr(t.keysize)+i*uintptr(t.elemsize))}if b.tophash[i] == emptyRest {break bucketloop}continue}k := add(unsafe.Pointer(b), dataOffset+i*uintptr(t.keysize))if t.indirectkey() {k = *((*unsafe.Pointer)(k))}// 匹配keyif !t.key.equal(key, k) {continue}if t.needkeyupdate() {typedmemmove(t.key, k, key)}elem = add(unsafe.Pointer(b), dataOffset+bucketCnt*uintptr(t.keysize)+i*uintptr(t.elemsize))goto done}ovf := b.overflow(t)if ovf == nil {break}// 遍历溢出桶b = ovf}// 桶溢出逻辑处理// 扩容 // 1. 装载因子超过 count/bucket>6.5 (key多桶少,B+1 双倍扩容)// 2. B<15 bucket=2^b overflow>bucket  B>=15 overflow>2^16 (桶多key分散,等量扩容减少overflow,紧凑key)if !h.growing() && (overLoadFactor(h.count+1, h.B) || tooManyOverflowBuckets(h.noverflow, h.B)) {hashGrow(t, h)goto again }// 当前桶满了if inserti == nil {// 创建或者使用之前的溢出桶保存数据newb := h.newoverflow(t, b)inserti = &newb.tophash[0]insertk = add(unsafe.Pointer(newb), dataOffset)elem = add(insertk, bucketCnt*uintptr(t.keysize))}// store new key/elem at insert positionif t.indirectkey() {kmem := newobject(t.key)*(*unsafe.Pointer)(insertk) = kmeminsertk = kmem}if t.indirectelem() {vmem := newobject(t.elem)*(*unsafe.Pointer)(elem) = vmem}typedmemmove(t.key, insertk, key)*inserti = toph.count++done://其他代码...return elem
}// 扩容
// runtime/map.go
func hashGrow(t *maptype, h *hmap) {bigger := uint8(1)if !overLoadFactor(h.count+1, h.B) {// 溢出桶过多,等量扩容bigger = 0h.flags |= sameSizeGrow}oldbuckets := h.buckets // 保存旧桶引用newbuckets, nextOverflow := makeBucketArray(t, h.B+bigger, nil) //创建新桶,溢出桶// 标记 h.buckets 将挂在 h.oldbucketsflags := h.flags &^ (iterator | oldIterator)if h.flags&iterator != 0 {flags |= oldIterator}h.B += biggerh.flags = flagsh.oldbuckets = oldbucketsh.buckets = newbucketsh.nevacuate = 0  // 迁移进度 0 h.noverflow = 0  // 溢出桶 0// extra更新// overflow 指向 oldoverflow// overflow 清空// nextOverflow 更新if h.extra != nil && h.extra.overflow != nil {// Promote current overflow buckets to the old generation.if h.extra.oldoverflow != nil {throw("oldoverflow is not nil")}h.extra.oldoverflow = h.extra.overflowh.extra.overflow = nil}if nextOverflow != nil {if h.extra == nil {h.extra = new(mapextra)}h.extra.nextOverflow = nextOverflow}// the actual copying of the hash table data is done incrementally// by growWork() and evacuate().
}// 搬迁bucket
// 迁移结构体
type evacDst struct {b *bmap          // 桶 指针i int            // 键值 索引k unsafe.Pointer // 健 指针e unsafe.Pointer // 值 指针
}//迁移数据函数
func evacuate(t *maptype, h *hmap, oldbucket uintptr) {// 定位老的bucket数组地址b := (*bmap)(add(h.oldbuckets, oldbucket*uintptr(t.bucketsize)))// 老的bucket桶数量newbit := h.noldbuckets()// 判断是否被搬迁过if !evacuated(b) {// xy contains the x and y (low and high) evacuation destinations.var xy [2]evacDstx := &xy[0]x.b = (*bmap)(add(h.buckets, oldbucket*uintptr(t.bucketsize)))x.k = add(unsafe.Pointer(x.b), dataOffset)x.e = add(x.k, bucketCnt*uintptr(t.keysize))if !h.sameSizeGrow() {// 双倍扩容,初始化第二个evacDst扩容y := &xy[1]y.b = (*bmap)(add(h.buckets, (oldbucket+newbit)*uintptr(t.bucketsize)))y.k = add(unsafe.Pointer(y.b), dataOffset)y.e = add(y.k, bucketCnt*uintptr(t.keysize))}// 遍历所有溢出桶for ; b != nil; b = b.overflow(t) {k := add(unsafe.Pointer(b), dataOffset)e := add(k, bucketCnt*uintptr(t.keysize))for i := 0; i < bucketCnt; i, k, e = i+1, add(k, uintptr(t.keysize)), add(e, uintptr(t.elemsize)) {top := b.tophash[i]if isEmpty(top) { //被搬迁过b.tophash[i] = evacuatedEmptycontinue}if top < minTopHash {throw("bad map state")}k2 := kif t.indirectkey() {k2 = *((*unsafe.Pointer)(k2))}var useY uint8if !h.sameSizeGrow() {//双倍扩容 //计算hash  rehashhash := t.hasher(k2, uintptr(h.hash0))if h.flags&iterator != 0 && !t.reflexivekey() && !t.key.equal(k2, k2) {// 1/2概率使用新桶useY = top & 1top = tophash(hash)} else {// hash溢出桶全部迁移新桶if hash&newbit != 0 {useY = 1}}}if evacuatedX+1 != evacuatedY || evacuatedX^1 != evacuatedY {throw("bad evacuatedN")}b.tophash[i] = evacuatedX + useY // evacuatedX + 1 == evacuatedYdst := &xy[useY]                 // evacuation destinationif dst.i == bucketCnt {dst.b = h.newoverflow(t, dst.b)dst.i = 0dst.k = add(unsafe.Pointer(dst.b), dataOffset)dst.e = add(dst.k, bucketCnt*uintptr(t.keysize))}dst.b.tophash[dst.i&(bucketCnt-1)] = top // mask dst.i as an optimization, to avoid a bounds checkif t.indirectkey() {*(*unsafe.Pointer)(dst.k) = k2 // copy pointer} else {typedmemmove(t.key, dst.k, k) // copy elem}if t.indirectelem() {*(*unsafe.Pointer)(dst.e) = *(*unsafe.Pointer)(e)} else {typedmemmove(t.elem, dst.e, e)}dst.i++dst.k = add(dst.k, uintptr(t.keysize))dst.e = add(dst.e, uintptr(t.elemsize))}}// 迁移完成 清理指针if h.flags&oldIterator == 0 && t.bucket.ptrdata != 0 {b := add(h.oldbuckets, oldbucket*uintptr(t.bucketsize))// Preserve b.tophash because the evacuation// state is maintained there.ptr := add(b, dataOffset)n := uintptr(t.bucketsize) - dataOffsetmemclrHasPointers(ptr, n)}}ß// 更新搬迁进度if oldbucket == h.nevacuate {advanceEvacuationMark(h, t, newbit)}
}
loadFactor %overflow bytes/entry hitprobe missprobe
4.00 2.13 20.77 3.00 4.00
4.50 4.05 17.30 3.25 4.50
5.00 6.85 14.77ß 3.50 5.00
5.50 10.55 12.94 3.75 5.50
6.00 15.27 11.67 4.00 6.00
6.50 20.90 10.79 4.25 6.50
7.00 27.14 10.15 4.50 7.00
  • loadFactor:负载因子
  • %overflow:溢出率,具有溢出桶 overflow buckets 的桶的百分比
  • bytes/entry:每个键值对所的字节数开销
  • hitprobe:查找存在的 key 时,平均需要检索的条目数量
  • missprobe:查找不存在的 key 时,平均需要检索的条目数量

todo

// 总结

http://www.zzvips.com/article/194814.html
https://zhuanlan.zhihu.com/p/412581873

指针类型 pointer

  • 引用类型
  • 取地址符是&
  • 指针保存的是地址,地址里的数据才是真正的值,使用*来获取值

int系列、float系列、bool、string、数组、结构体struct,一般在栈

指针、切片、管道、接口、Map是引用类型,一般在堆,GC回收

声明

//空指针 nil
var ptr *int//零指针
ptr1 := new(int)

取指针

// 获取指针 i的地址
i:=1
ptr:=&i
// 获取指针的指针 ptr的地址
ptr2:=&ptr

内存

类型判断

任何类型都是实现了interface空接口,interface可以承接任何类型后进行类型断言

comma-ok断言

// value ,ok := interface{}[(container)].(fieldtype)
// value container值
// ok 类型断言是否正确
// fieldtype 断言类型v,ok:=interface{}("我是string").(string)
println(v,ok)  // "我是string" true

type - switch

/*
// data.(type) 获取类型
switch value := interface{}[(container)].(type) {case int:case string:...
}
*/
switch value := interface{}("我是string").(type) {case int:case string:println(value)
}
// string

golang 数据类型相关推荐

  1. Golang——数据类型使用细节详解

    数据类型: 计算机存储设备最小信息单位是位(bit),最小的存储单元是字节(byte) 占用字节的不同,所表示能存储的数据长度不同 作用: 说明数据的数据的结构,便于后面定义变量.参数传递等 默认值: ...

  2. golang 数据类型之间的转换

    一.基本数据类型之间的转换 1.string到int int,err:=strconv.Atoi(string) 2.string到int64 int64, err := strconv.ParseI ...

  3. 2.7——golang数据类型【字符串类型】

    基本介绍 字符串就是一串固定长度的字符连接起来的字符序列.Go的字符串是由单个字节链接起来的. Go语言的字符串的字节使用utf-8编码标识Unicode文本 package main import ...

  4. golang 数据类型 简介

    目录 bool 有符号整型 无符号整型 浮点型 复数类型 其他数字类型 string 类型 下面是 Go 支持的基本类型: bool 数字类型 int8, int16, int32, int64, i ...

  5. golang数据类型与MySQL数据类型的对应

    本文原创文章,转载注明出处,博客地址 https://segmentfault.com/u/to... 第一时间看后续精彩文章.觉得好的话,顺手分享到朋友圈吧,感谢支持.

  6. java i数据类型_数据类型 I

    都来自菜鸟教程,自己总结学习使用, JS 数据类型  没有对比就没有伤害, 字符串(String).数字(Number).布尔(Boolean).数组(Array).对象(Object).空(Null ...

  7. 使用Golang搭建gRPC服务提供给.NetCore客户端调用

    gRPC概述 RPC 说到gRPC就不得不提RPC,所谓RPC(remote procedure call 远程过程调用)框架实际是提供了一套机制,使得应用程序之间可以进行通信,简单点来说就是我A机器 ...

  8. golang int64转string_(一)Golang从入门到原地起飞

    1.Golang 变量定义方法: 1)定义变量 变量名 类型 = 表达式 var go string = "hello" 2)在函数内部,可以使用更简略的 := 方式声明并初始化变 ...

  9. Golang 的类型与零值

    Golang 中定义不同类型的变量,不是通过声明就是通过 make 或 new . 未显式初始化时,将被赋予一个默认值,该默认值便为该类型的零值.不同的类型有不同的零值. 类型 类型名 零值 字符类型 ...

最新文章

  1. linux centos grub grub2 加密、清除
  2. 最短路径算法整理(二)
  3. 清华90后博士后26岁已在《科学》发文6篇
  4. ORA-01033 的解决
  5. python write函数换行_Python基础知识(三)
  6. 【Cocos2dx】资源目录,播放背景音乐,导入外部库
  7. 使用GMM进行语音性别检测(入门)
  8. 大话数据结构(个人笔记)
  9. Win 10 蓝屏,出现DRIVER_POWER_STATE_FAILURE的解决方法
  10. 西瓜数据集介绍以及获取。
  11. Java源文件编译出错:类文件包含错误的类 请删除该文件或确保文件位于正确的类路径子目录中
  12. matlab求两向量夹角_【求精干货】高中数学知识点总结归纳高一学生必须掌握
  13. 掌上题库V1.2.2全开源版本
  14. CSDN下载频道2013下半年超人气精华资源汇总
  15. js 你知道为什么[]==![]是成立的吗(强制类型转换)
  16. 二、Unity编辑器开发之ContextMenu
  17. css 按空格键对按钮暂停,当按下回车键后,怎么清空回车键的空格,或者模拟发送按键让光标向上?...
  18. VScode运行js时,出现 'node' �����
  19. 如何从ip服务器所用系统,系统运维|如何使用phpIPAM来管理IP地址和子网
  20. Java使用hotmail的SMTP服务器转发邮件出错

热门文章

  1. 通信原理:数字信号的载波传输
  2. xml.parsers.expat.ExpatError: mismatched tag: line 63, column 4
  3. 关于anaconda navigator打不开问题的解决
  4. linux在内核中设置开机logo
  5. 罗马仕php30和wa30,容量大 多接口,充电宝中的战斗机 — 罗马仕WA30充电宝测评...
  6. 【Unity】Unity 生命周期
  7. Java基础(19)数据结构概述、ArrayList集合、Vector集合、LinkedList集合、集合框架练习
  8. 鸿蒙OS安卓版,多款纯鸿蒙OS应用已上线,体积小、运行更流畅,有鸿蒙OS专属图标...
  9. 学生大本营社区运营专员招聘公告
  10. Linux进阶学习笔记之——局域网控制者:Proxy(代理)服务器