You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

110 lines
12 KiB
XML

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

<?xml version="1.0" encoding="utf-8" standalone="yes"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
<channel>
<title>数据类型与数据结构 on 深入Go语言之旅</title>
<link>https://go.cyub.vip/type/</link>
<description>Recent content in 数据类型与数据结构 on 深入Go语言之旅</description>
<generator>Hugo -- gohugo.io</generator>
<language>zh-cn</language><atom:link href="https://go.cyub.vip/type/index.xml" rel="self" type="application/rss+xml" />
<item>
<title>nil类型</title>
<link>https://go.cyub.vip/type/nil/</link>
<pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate>
<guid>https://go.cyub.vip/type/nil/</guid>
<description>nil # 在探究 nil 之前,我们先看看零值的概念。
零值 # 零值(zero value)1 指的是当声明变量且未显示初始化时Go语言会自动给变量赋予一个默认初始值。对于值类型变量来说不同值类型有不同的零值比如整数型零值是 0字符串类型是 &amp;quot;&amp;quot;,布尔类型是 false。对于引用类型变量其零值都是 nil。
类型 零值 数值类型 0 字符串 &amp;quot;&amp;quot; 布尔类型 false 指针类型 nil 通道 nil 函数 nil 接口 nil 映射 nil 切片 nil 结构体 每个结构体字段对应类型的零值 自定义类型 其底层类型的对应的零值 从零值的定义可以看出Go语言引入 nil 概念,是为了将其作为引用类型变量的零值而存在。
nil # nil 是Go语言中的一个变量是预先声明的标识符用来作为引用类型变量的零值。
// nil is a predeclared identifier representing the zero value for a // pointer, channel, func, interface, map, or slice type. var nil Type // Type must be a pointer, channel, func, interface, map, or slice type nil 不能通过:=方式赋值给一个变量,下面代码是编译不通过的:</description>
</item>
<item>
<title>切片</title>
<link>https://go.cyub.vip/type/slice/</link>
<pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate>
<guid>https://go.cyub.vip/type/slice/</guid>
<description>切片 # 切片是Go语言中最常用的数据类型之一它类似数组但相比数组它更加灵活高效由于它本身的特性往往也更容易用错。
不同于数组是值类型而切片是引用类型。虽然两者作为函数参数传递时候都是值传递pass by value但是切片传递的包含数据指针可以细分为pass by pointer如果切片使用不当会产生意想不到的副作用。
初始化 # 切片的初始化方式可以分为三种:
使用make函数创建切片
make函数语法格式为make([]T, length, capacity)capacity可以省略默认等于length
使用字面量创建切片
从数组或者切片派生(reslice)出新切片
Go支持从数组、指向数组的指针、切片类型变量再reslice一个新切片。
reslice操作语法可以是[]T[low : high],也可以是[]T[low : high : max]。其中low,high,max都可以省略low默认值是0high默认值cap([]T)max默认值cap([]T)。low,hight,max取值范围是0 &amp;lt;= low &amp;lt;= high &amp;lt;= max &amp;lt;= cap([]T)其中high-low是新切片的长度max-low是新切片的容量。
对于[]T[low : high],其包含的元素是[]T中下标low开始到high结束不含high所在位置的相当于左闭右开[low, high)的元素元素个数是high - low个容量是cap([]T) - low。
func main() { slice1 := make([]int, 0) slice2 := make([]int, 1, 3) slice3 := []int{} slice4 := []int{1: 2, 3} arr := []int{1, 2, 3} slice5 := arr[1:2] slice6 := arr[1:2:2] slice7 := arr[1:] slice8 := arr[:1] slice9 := arr[3:] slice10 := slice2[1:2] fmt.</description>
</item>
<item>
<title>字符串</title>
<link>https://go.cyub.vip/type/string/</link>
<pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate>
<guid>https://go.cyub.vip/type/string/</guid>
<description>字符串 # 我们知道C语言中的字符串是使用字符数组 char[] 表示,字符数组的最后一位元素是 \0用来标记字符串的结束。C语言中字符串的结构简单但获取字符串长度时候需要遍历字符数组才能完成。
Go语言中字符串的底层结构中也包含了字符数组该字符数组是完整的字符串内容它不同于C语言字符数组中没有标记字符串结束的标记。为了记录底层字符数组的大小Go语言使用了额外的一个长度字段来记录该字符数组的大小字符数组的大小也就是字符串的长度。
数据结构 # Go语言字符串的底层数据结构是 reflect.StringHeader( reflect/value.go),它包含了指向字节数组的指针,以及该指针指向的字符数组的大小:
type StringHeader struct { Data uintptr Len int } 字符串复制 # 当将一个字符串变量赋值给另外一个变量时候,他们 StringHeader.Data 都指向同一个内存地址,不会发生字符串拷贝:
a := &amp;#34;hello&amp;#34; b := a 从上图中我们可以看到a变量和b变量的Data字段存储的都是0x1234而0x1234是字符数组的起始地址。
接来下我们借助 GDB 工具来验证Go语言中字符串数据结构是不是按照上面说的那样。
package main import ( &amp;#34;fmt&amp;#34; ) func main() { a := &amp;#34;hello&amp;#34; b := a fmt.Printf(&amp;#34;a变量地址%p\n&amp;#34;, &amp;amp;a) fmt.Printf(&amp;#34;b变量地址%p\n&amp;#34;, &amp;amp;b) print(&amp;#34;断点打在这里&amp;#34;) } 将上面代码构建二进制应用, 然后使用 GDB 调试一下:
go build -o string string.go # 构建二进制应用 gdb .</description>
</item>
<item>
<title>指针</title>
<link>https://go.cyub.vip/type/pointer/</link>
<pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate>
<guid>https://go.cyub.vip/type/pointer/</guid>
<description>指针 # Golang支持指针但是不能像C语言中那样进行算术运算。对于任意类型T其对应的的指针类型是*T类型T称为指针类型*T的基类型。
引用与解引用 # 一个指针类型*T变量B存储的是类型T变量A的内存地址我们称该指针类型变量B引用(reference)了A。从指针类型变量B获取或者称为访问A变量的值的过程叫解引用。解引用是通过解引用操作符*操作的。
func main() { var A int = 100 var B *int = &amp;amp;A fmt.Println(A == *B) } 转换和可比较性 # 对于指针类型变量能不能够比较和显示转换需要满足以下规则:
指针类型*T1和*T2相应的基类型T1和T2的底层类型必须一致。 type MyInt int type PInt *int type PMyInt *MyInt func main() { p1 := new(int) var p2 PInt = p1 // p2底层类型是*int p3 := new(MyInt) var p4 PMyInt = p3 // p4底层类型是*MyInt fmt.Println(p1, p2, p3, p4) } uintptr # uintptr是一个足够大的整数类型能够存放任何指针。不同C语言Go语言中普通类型指针不能进行算术运算我们可以将普通类型指针转换成uintptr然后进行运算但普通类型指针不能直接转换成uintptr必须先转换成unsafe.Pointer类型之后再转换成uintptr。
// uintptr is an integer type that is large enough to hold the bit pattern of // any pointer.</description>
</item>
<item>
<title>数组</title>
<link>https://go.cyub.vip/type/array/</link>
<pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate>
<guid>https://go.cyub.vip/type/array/</guid>
<description>数组 # 数组是Go语言中常见的数据结构相比切片数组我们使用的比较少。
初始化 # Go语言数组有两个声明初始化方式一种需要显示指明数组大小另一种使用 ...保留字, 数组的长度将由编译器在编译阶段推断出来:
arr1 := [3]int{1, 2, 3} // 使用[n]T方式 arr2 := [...]int{1, 2, 3} // 使用[...]T方式 arr3 := [3]int{2: 3} // 使用[n]T方式 arr4 := [...]int{2: 3} // 使用[...]T方式 注意:
上面代码中 arr3 和 arr4 的初始化方式是指定数组索引对应的值。实际使用中这种方式并不常见。
可比较性 # 数组大小是数组类型的一部分,只有数组大小和数组元素类型一样的数组才能够进行比较。
func main() { var a1 [3]int var a2 [3]int var a3 [5]int fmt.Println(a1 == a2) // 输出true fmt.Println(a1 == a3) // 不能够比较,会报编译错误: invalid operation: a1 == a3 (mismatched types [3]int and [5]int) } 值类型 # Go语言中数组是一个值类型变量将一个数组作为函数参数传递是拷贝原数组形成一个新数组传递在函数里面对数组做任何更改都不会影响原数组</description>
</item>
<item>
<title>映射类型</title>
<link>https://go.cyub.vip/type/map/</link>
<pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate>
<guid>https://go.cyub.vip/type/map/</guid>
<description>映射 # 映射也被称为哈希表(hash table)、字典。它是一种由key-value组成的抽象数据结构。大多数情况下它都能在O(1)的时间复杂度下实现增删改查功能。若在极端情况下出现所有key都发生哈希碰撞时则退回成链表形式此时复杂度为O(N)。
映射底层一般都是由数组组成该数组每个元素称为桶它使用hash函数将key分配到不同桶中若出现碰撞冲突时候则采用链地址法也称为拉链法或者开放寻址法解决冲突。下图就是一个由姓名-号码构成的哈希表的结构图:
Go语言中映射中key若出现冲突碰撞时候则采用链地址法解决Go语言中映射具有以下特点
引用类型变量 读写并发不安全 遍历结果是随机的 数据结构 # Go语言映射的数据结构 Go语言中映射的数据结构是 runtime.hmap( runtime/map.go):
// A header for a Go map. type hmap struct { count int // 元素个数用于len函数返回map元素数量 flags uint8 // 标志位标志当前map正在写等状态 B uint8 // buckets个数的对数即桶数量 = 2 ^ B noverflow uint16 // overflow桶数量的近似值overflow桶即溢出桶即链表法中存在链表上的桶的个数 hash0 uint32 // 随机数种子用于计算key的哈希值 buckets unsafe.Pointer // 指向buckets数组如果元素个数为0时该值为nil oldbuckets unsafe.Pointer // 扩容时指向旧的buckets nevacuate uintptr // 用于指示迁移进度,小于此值的桶已经迁移完成 extra *mapextra // 额外记录overflow桶信息 } 映射中每一个桶的结构是 runtime.bmap( runtime/map.</description>
</item>
<item>
<title>空结构体</title>
<link>https://go.cyub.vip/type/empty_struct/</link>
<pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate>
<guid>https://go.cyub.vip/type/empty_struct/</guid>
<description>空结构体 # 空结构体指的是没有任何字段的结构体。
大小与内存地址 # 空结构体占用的内存空间大小为零字节,并且它们的地址可能相等也可能不等。当发生内存逃逸时候,它们的地址是相等的,都指向了 runtime.zerobase。
// empty_struct.go type Empty struct{} //go:linkname zerobase runtime.zerobase var zerobase uintptr // 使用go:linkname编译指令将zerobase变量指向runtime.zerobase func main() { a := Empty{} b := struct{}{} fmt.Println(unsafe.Sizeof(a) == 0) // true fmt.Println(unsafe.Sizeof(b) == 0) // true fmt.Printf(&amp;#34;%p\n&amp;#34;, &amp;amp;a) // 0x590d00 fmt.Printf(&amp;#34;%p\n&amp;#34;, &amp;amp;b) // 0x590d00 fmt.Printf(&amp;#34;%p\n&amp;#34;, &amp;amp;zerobase) // 0x590d00 c := new(Empty) d := new(Empty) fmt.Sprint(c, d) // 目的是让变量c和d发生逃逸 println(c) // 0x590d00 println(d) // 0x590d00 fmt.Println(c == d) // true e := new(Empty) f := new(Empty) println(e) // 0xc00008ef47 println(f) // 0xc00008ef47 fmt.</description>
</item>
</channel>
</rss>