
16.派生数据类型
1.数组
var 数组名 [元素个数] 数据类型
var arr [10]int
var array [5] int // 全部为 0 var array [5] *int // 指针类型 var array := [5] int {1,2,3,4,5} // 初始化 var array := [5] {1:1, 4:5} // 初始化 1, 5 var array := [...] {1,2,3,4,5} // 长度根据初始化确定 var array [5][2] int 二维数组
- 数组特点
长度固定,不能修改
赋值和函数传递过程是值复制,涉及到内存 copy,性能低下
- 数组在内存中的地址:
func main(){ brr := [10]int{0,1,2,3,4,5,6,7,8,9} for i := 0; i < len(brr); i++ { //& 取地址运算符 fmt.Println(&brr[i]) } } C:\Users\vSphere\go\src\day02>go run 02.go 0xc000016230 0xc000016238 0xc000016240 0xc000016248 0xc000016250 0xc000016258 0xc000016260 0xc000016268 0xc000016270 0xc000016278
- 数组的遍历
package main import "fmt" func main() { //数组定义和使用 //var 数组名字 [元素的个数] 数据的类型 //var arr [10]int 默认值为0 //使用数组名+下标进行数组初始化 下标是从0开始到数组最大元素-1 //arr[0] = 123 //数据下标越界 arr[-1] = 123 //数组的初始化方式 var arr [10]int = [10]int{1, 2, 3, 4, 5, 6, 7, 8, 9, 0} brr := [10]int{0, 1, 2, 3, 4, 5, 6, 7, 8, 9} fmt.Println(arr) fmt.Println(brr) /* C:\Users\vSphere\go\src\day02>go run 02.go [1 2 3 4 5 6 7 8 9 0] [0 1 2 3 4 5 6 7 8 9] */ //len(数组名) 计算数组元素个数 fmt.Println(len(arr)) //遍历数组元素 //方法1 for i := 0; i < 10; i++ { fmt.Println(arr[i]) } //方法2 i=index下标 v=value值 for i, v := range arr { fmt.Printf("arr[%d]=%d\n", i, v) } }
2.切片
切片的定义和使用
var 切片名 [ ]数据类型
- slice声明方式
slice := make([]int, 5) slice2 := make([]int, 0, 5)
- slice数据结构
type slice struct { array unsafe.Pointer len int //当前存储长度 cap int //可用长度 }
- 计算方法
切片名[起始下标:结束下标:容量] 左闭右开 包含起始下标 不包含结束下标
s3 := s1[i:j] len : j - i cap : k - i , k为s1的长度
- 切片追加
slice = append(slice, 1, 2, 3) slice = append(slice, slice2...)
- 示例
package main import "fmt" func main() { /* 切片的定义和使用 var 切片名字 []数据类型 var slice []int make([]数据类型,长度) var slice []int = make([]int,10) slice[0] = 123 fmt.Println(slice) C:\Users\vSphere\go\src\day02>go run 03.go [123 0 0 0 0 0 0 0 0 0] */ var slice []int fmt.Println(len(slice)) //计算长度 fmt.Println(cap(slice)) //计算容量 //使用append对切片进行扩容 slice = append(slice, 1, 2, 3, 4, 5) fmt.Println(len(slice)) //计算长度 fmt.Println(cap(slice)) //计算容量 /* C:\Users\vSphere\go\src\day02>go run 03.go 0 0 5 6 */ //切片扩容为偶数的值 小于1024为上一次的2倍 大于1024为上一次的1/4 fmt.Println(slice) }
- 切片的截取
切片的截取 是将新的切片指向源切片的内存地址 修改一个会影响另外一个
package main import "fmt" func main() { //切片的截取 slice := []int{1, 2, 3, 4, 5, 6, 7, 8, 9, 0} // 切片名[起始下标:结束下标:容量] 左闭右开 包含起始下标 不包含结束下标 s := slice[2:7] fmt.Println(s) s = slice[:7] fmt.Println(s) s = slice[2:] fmt.Println(s) s = slice[2:5:6] //实际容量=容量-起始下标 fmt.Println(s) //实际容量=容量-起始下标 fmt.Println(cap(s)) /* C:\Users\vSphere\go\src\day02>go run 04.go [3 4 5 6 7] [1 2 3 4 5 6 7] [3 4 5 6 7 8 9 0] [3 4 5] 4 */ }查看占用空间
切片的截取可以确认是地址传递,会出现互相影响,但是append()之后会重新拷贝一份并不会互相影响;
package main import ( "fmt" "unsafe" ) func main() { //切片的截取 slice := []int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10} s := slice[2:5:6] //实际容量=容量-起始下标 s[0] = 333 //切片的截取 是将新的切片指向源切片的内存地址 修改一个会影响另外一个 fmt.Println(s) fmt.Println(slice) fmt.Println(unsafe.Sizeof(slice)) fmt.Println(unsafe.Sizeof(s)) /* C:\Users\vSphere\go\src\day02>go run 05.go [333 4 5] [1 2 333 4 5 6 7 8 9 10] 24 24 */ }
- unsafe.Sizeof()函数解释:
Sizeof接受任何类型的表达式x,并返回一个假设的变量的大小(以字节v好像被宣布通过var v = x。不包括任何内存大小可能引用的x。例如,如果x是一片,运算符返回的大小块描述符,而不是引用的内存块的大小。
查看占用空间
- copy()在存储存储两个内容完全相同 但是不会相互影响
func main() { slice := []int{1, 2, 3, 4, 5} //s := slice //s[2] = 333 //fmt.Println(s) //fmt.Println(slice) //在存储存储两个内容完全相同 但是不会相互影响 s := make([]int, 5) copy(s, slice) s[2]=333 fmt.Println(s) fmt.Println(slice) } C:\Users\vSphere\go\src\day02>go run 05.go [1 2 333 4 5] [1 2 3 4 5]
3.map
底层基于 Hash 实现,基于 Key-Value,无序的数据集合
dict := make(map[T_KEY]T_VALUE)
var 字典名字 map[键类型]值类型
key 的类型需要具备 == 和 != 操作
函数类型、字典类型和切片类型不能作为 key,不支持的操作类型会导致 panic
- 检查值是否存在
package main func main() { m := make(map[string]bool) _, h := m["hello"] if !h { } if _, h := m["hello"]; h { } }var m map[string]int
// nil 类型,添加和修改会导致 panic
nil: len/map[key]/delete(m, key)// 可以正常工作
map 默认并发不安全,多个 goroutine 写同一个 map,引发竞态错误, go run –race 或者go build - racemap 对象即使删除了全部的 key,但不会缩容空间
- map的定义和使用
package main import "fmt" func main() { //map的定义 //var 字典名字 map[键类型]值类型 var n map[int]string = make(map[int]string, 10) n[1001] = "x86" n[1002] = "cloud" fmt.Println(n) /* C:\Users\vSphere\go\src\day02>go run 06.go map[1001:x86 1002:cloud] */ //map是一个无序的集合 m := map[int]string{1001: "x86", 1002: "cloud", 1003: "com", 1004: "cn"} fmt.Println(m) /* C:\Users\vSphere\go\src\day02>go run 06.go map[1001:x86 1002:cloud 1003:com 1004:cn] */ for k, v := range m { fmt.Println(k, v) } /* C:\Users\vSphere\go\src\day02>go run 06.go 1004 cn 1001 x86 1002 cloud 1003 com */ }
- 判断map中是否存在value
func main() { //map中的key必须支持 ‘== ! =’运算操作符 不能是切片 map函数 m := make(map[int]string) m[1001] = "x86" m[1002] = "cloud" //通过key判断value是否存在 if v, ok := m[1001]; ok { fmt.Println(v) } else { fmt.Println("不存在key") } }
- map中进行删除键值操作delete()
func main() { //map中的key必须支持 ‘== ! =’运算操作符 不能是切片 map函数 m := make(map[int]string) m[1001] = "x86" m[1002] = "cloud" //map原始长度以及键值 fmt.Println(m) fmt.Println(len(m)) //删除map中的数据 delete(m, 1001) //删除后的长度以及键值 fmt.Println(len(m)) fmt.Println(m) fmt.Println(unsafe.Sizeof(m)) }
4.指针
- 只要将数据存储在内存中都会为其分配内存地址。内存地址使用十六进数据表示
- 内存为每一个字节分配一个32位或64位的编号(与32位或者64位处理器相关)
- 可以使用运算符 & (取地址运算符)来获取数据的内存地址。
//定义变量 var i int = 10 //使用格式化打印变量的内存地址 //%p是一个占位符 输出一个十六进制地址格式 fmt.Printf("%p\n", &i)如果想将获取的地址进行保存,应该怎样做呢?
可以通过指针变量来存储,所谓的指针变量:就是用来存储任何一个值的内存地址。//定义指针变量 var 指针变量名 //默认初始值为nil 指向内存地址编号为0的空间 var 指针变量名 *数据类型 = &变量package main import "fmt" func main() { var i int = 10 //指针类型变量 //指针变量也是变量 指针指向了变量的内存地址 //对变量取地址 将结果赋值给指针变量 var p *int = &i //打印指针变量p的值 同时也是i的地址 fmt.Println(p) }指针变量,除了有正确指向,还可以通过new( )函数来指向。
具体的应用方式如下:根据数据类型 创建内存空间 返回值为数据类型对应的指针
new(数据类型)new( )函数的作用就是动态分配空间,不需要关心该空间的释放,Go语言会自动释放。
func main() { var p *int //创建一个int大小的内存空间 返回值为*int p = new(int) *p = 123 //打印值 fmt.Println(*p) //打印地址 fmt.Println(p) }
- 通过指针间接修改变量值
package main import "fmt" func main() { a := 10 //取出变量在内存中的值 fmt.Println(&a) //指针的定义以及输出 var p *int = &a fmt.Println(p) fmt.Println(*p) /* C:\Users\vSphere\go\src\day02>go run 07.go 0xc00000a0d0 0xc00000a0d0 10 */ //通过指针间接修改变量的值 *p = 123 fmt.Println(a) //不可以获取常量的地址 const Max int = 100 fmt.Println(&Max) /* C:\Users\vSphere\go\src\day02>go run 07.go # command-line-arguments .\07.go:28:14: cannot take the address of Max */ }
- 定义指针,默认值为nil指向内存地址编号为0的空间
- 内存地址0-255为系统占用 不允许用户读写操作
package main import "fmt" func main(){ //定义指针,默认值为nil,指向内存地址编号为0的空间 //内存地址0-255为系统占用,不允许用户读写 var p * int =nil var k *int =nil //*和int可以不留空格 fmt.Println(p) fmt.Println(k) //给编号为0的内存地址空间进行存值 *p = 123 //触发panic fmt.Println(*p) /* panic: runtime error: invalid memory address or nil pointer dereference [signal 0xc0000005 code=0x1 addr=0x0 pc=0x49df74] goroutine 1 [running]: main.main() C:/Users/vSphere/go/src/day02/07.go:13 +0xd4 exit status 2 */ //开辟数据类型大小的空间 返回值为指针类型 var k *int //new(数据类型) k = new(int) //动态 *k =123 fmt.Println(*k) } C:\Users\vSphere\go\src\day02>go run 07.go <nil> 123