广州番禺区网站建设,腾讯企业邮箱免费注册入口,秦皇岛网站优化,wordpress文章发布器目录字符串数组数组初始化指针复制切片基本操作resliceappendcopy字典deletemap是引用类型并发操作字符串
字符串是不可变字节#xff08;byte#xff09;序列#xff0c;其本身是一个复合结构
type stringStruct struct{str unsafe.Pointerlen int
}头部指针指向字节数组…
目录字符串数组数组初始化指针复制切片基本操作resliceappendcopy字典deletemap是引用类型并发操作字符串
字符串是不可变字节byte序列其本身是一个复合结构
type stringStruct struct{str unsafe.Pointerlen int
}头部指针指向字节数组但没有null结尾默认以utf-8编码粗出Unicode字符字面量李允许使用十六进制、八进制和UTF编码格式。默认值是而不是null。
使用定义不做转移处理的原始字符串支持跨行
func main() {s : line\r\n,line 2println(s)
}支持!、、、、、操作符允许以索引号访问字节数组但不能获取元素地址以切片语法返回子串时其内部依旧指向原字节数组使用for遍历字符串时分byte和rune两种方式
func main() {s : 哈喽for i : 0; i len(s); i { // bytefmt.Printf(%d: [%c]\n, i, s[i])}for i, c : range s { // rune 返回数组索引号 以及unicode字符fmt.Printf(%d: [%c]\n, i, c)}0: [å]1: [ ]2: [ ]3: [å]4: [ ]5: [½]0: [哈]3: [喽]}数组
定义数组类型时数组长度必须是非负整形常量表达式长度是类型组成部分所以元素类型相同但是长度不同的数组不属于同一类型
func main() {d1 : [3]int{}d2 : [2]int{}d1 d2 // cannot use d2 (variable of type [2]int) as type [3]int in assignment
}数组初始化 var a [4]int // 元素自动初始化为0 [0 0 0 0]b : [4]int{2, 5} // 未提供初始值得元素自动初始化为0 [2 5 0 0]c : [4]int{5, 3: 100} // 可指定索引位置初始化 [5 0 0 100]d : [...]int{1, 2, 3} // 编译器按初始化值数量确定数组长度 [1 2 3]e : [...]int{10, 3: 100} // 支持索引初始化但是数组长度与此有关 [10 0 0 100]fmt.Println(a, b, c, d, e)对于结构等复合类型可省略元素初始化类型标签 type user struct {name stringage byte}d : [...]user{{Tome, 20}, // 省略了类型标签{Mary, 18}}fmt.Printf(%#v\n, d)在定义多维数组时仅第一维度允许使用“…” a : [2][2]int{{1, 2},{3, 4},}b : [...][2]int{{10, 20},{30, 40},}c : [...][2][2]int{{{1, 2},{3, 4},},{{10, 20},{30, 40},},}fmt.Println(a)fmt.Println(b)fmt.Println(c)[[1 2] [3 4]][[10 20] [30 40]][[[1 2] [3 4]] [[10 20] [30 40]]]内置函数len和cap都返回第一维度长度 如果元素类型支持、!操作符那么数组也支持此操作
指针
要分清楚指针数组和数组指针的区别指针数组是指元素为指针类型的数组数组指针是获取数组变量的地址。 x, y : 10, 20a : [...]*int{x, y} // 指针数组p : a // 数组指针fmt.Printf(%T,%v\n, a, a) //[2]*int,[0xc000018088 0xc0000180a0]fmt.Printf(%T,%v\n, p, p) //*[2]*int,[0xc000018088 0xc0000180a0]// 获取任意元素地址println(a, a[0], a[1])//数组指针可以直接用来操作元素c : [...]int{1, 2}d : cd[1] 10println(d[1])复制
Go数组是值类型赋值和传参操作都会复制整个数组数据。
func test(a [3]int) {println(a, a[0], a[1], a[2]) // 传参 0xc0000cbf40 0xc0000cbf40 0xc0000cbf48 0xc0000cbf50
}func main() {a : [...]int{1, 2, 3}println(a, a[0], a[1], a[2]) // 0xc0000cbf58 0xc0000cbf58 0xc0000cbf60 0xc0000cbf68b : aprintln(b, b[0], b[1], b[2]) //赋值 0xc0000cbf28 0xc0000cbf28 0xc0000cbf30 0xc0000cbf38test(a)如果需要可改用指针或者切片以避免数据复制
func test(a *[3]int) {println(a, a[0], a[1], a[2]) // 指针传参 0xc00007bf50 0xc00007bf38 0xc00007bf40 0xc00007bf48
}func main() {a : [...]int{1, 2, 3}println(a, a[0], a[1], a[2]) // 0xc00007bf38 0xc00007bf38 0xc00007bf40 0xc00007bf48b : a[:]println(b, b[0], b[1], b[2]) //切片赋值 0xc00007bf58 0xc00007bf38 0xc00007bf40 0xc00007bf48var c a // 指针赋值 0xc00007bf68 0xc00007bf38 0xc00007bf40 0xc00007bf48println(c, c[0], c[1], c[2])test(a)切片
基本操作
切片本身并非动态数组或数组指针内部通过指针引用底层数组设定相关属性将数据读写操作限定在指定区域内。其本身是个只读对象工作机制类似数组指针的一种包装。 x : [...]int{0, 1, 2, 3, 4, 5, 6, 7, 8, 9}x1 : x[:]x2 : x[2:5]x3 : x[2:5:7]x4 : x[4:]x5 : x[:4]x6 : x[:4:6]fmt.Println(x1, len(x1), cap(x1)) // [0 1 2 3 4 5 6 7 8 9] 10 10fmt.Println(x2, len(x2), cap(x2)) // [2 3 4] 3 8fmt.Println(x3, len(x3), cap(x3)) // [2 3 4] 3 5fmt.Println(x4, len(x4), cap(x4)) // [4 5 6 7 8 9] 6 6fmt.Println(x5, len(x5), cap(x5)) // [0 1 2 3] 4 10fmt.Println(x6, len(x6), cap(x6)) // [0 1 2 3] 4 6属性cap表示切片所引用数组片段的真实长度len用于限定可读的写元素数量。 cap表示切片引用数组的容量该数组可以插入多少个元素如果cap不够则需要扩容 x3 append(x3, 1000)x3 append(x3, 1001)fmt.Println(x3, len(x3), cap(x3)) // [2 3 4 1000 1001] 5 5println(x3)x3 append(x3, 1002)fmt.Println(x3, len(x3), cap(x3)) //cap不够发生扩容cap变成10 [2 3 4 1000 1001 1002] 6 10println(x3)可以直接创建切片对象无须预先准备数组因为是引用类型须使用make函数或显 s1 : make([]int, 3, 5) // 指定len、cap底层数组初始化为零值s2 : make([]int, 3) // 省略cap 和len 相等s3 : []int{10, 20, 5: 30} // 按初始化元素分配底层数组并设置 len、capfmt.Println(s1, len(s1), cap(s1)) // [0 0 0] 3 5fmt.Println(s2, len(s2), cap(s2)) // [0 0 0] 3 3fmt.Println(s3, len(s3), cap(s3)) // [10 20 0 0 0 30] 6 6可以获取元素地址但是不能向数组那样直接用指针访问元素内容 s : []int{0, 1, 2, 3, 4}p : sp0 : s[0]p1 : s[1]println(p, p0, p1) // 0xc00007bf58 0xc00000a360 0xc00000a368(*p)[0] 100 // *[]int 不支持索引操作 须先返回[]int对象 mismatched types []int and untyped int*p1 100fmt.Println(s) // [100 101 2 3 4]reslice
对切片再次进行切片不能超出cap但是不受len限制。新切片对象依旧指向原底层数组也就是说修改对所有关联切片可见。 d : [...]int{0, 1, 2, 3, 4, 5, 6, 7, 8, 9}s1 : d[3:7]s2 : s1[:3]s2[1] 100println(s1[0]) // 0xc000014298println(s2[0]) // 0xc000014298fmt.Println(s1) // [3 4 100 6]fmt.Println(s2) // [3 100 5] append
向切片尾部添加数据返回新的切片对象 s : make([]int, 0, 5)s1 : append(s, 10)s2 : append(s1, 20, 30)fmt.Println(s, len(s), cap(s)) // [] 0 5fmt.Println(s1, len(s1), cap(s1)) // [10] 1 5fmt.Println(s2, len(s2), cap(s2)) // [10 20 30] 3 5数据被追加到原底层数组如超出cap限制则为新切片对象重新分配数组。
copy
在两个切片对象间复制数据允许指向同一底层数组允许目标区间重叠最终所复制长度以较短的切片长度为准。
d : []int{0, 1, 2, 3, 4, 5, 6, 7, 8, 9}s1 : d[5:8]n : copy(d[4:], s1) // 在同一底层数组的不同区间复制fmt.Println(n, d) // 3 [0 1 2 3 5 6 7 7 8 9]s2 : make([]int, 6)n copy(s2, d) // 在不同数组间复制fmt.Println(n, s2) // 6 [0 1 2 3 5 6]字典
字典是引用类型使用make函数或初始化表达语句来创建。 m : map[string]int{a: 1,b: 2,}fmt.Println(m)m[a] 10 // 修改m[c] 30 // 新增if v, ok : m[d]; ok { // 使用ok-idiom 判断key是否存在println(v)}delete(m, d) // 删除键值对不存在时不会出错访问不存在的键值对默认返回零值不会引发错误但推荐使用ok-idiom模式毕竟通过零值无法判断键值是否存在或许存储的value本身就是零。 因内存访问安全和河西算法等缘故字典被设计成“not addressable” 故不能直接修改value成员。 正确做法时返回整个value待修改后再设置字典键值或直接用指针类型。 type user struct {name stringage byte}m : map[int]user{1: {Tome, 19},}//m[1].age 1 // Cannot assign to m[1].ageu : m[1]u.age 1m[1] ufmt.Println(m) // map[1:{Tome 20}]m2 : map[int]*user{1: user{Jack, 20},}m2[1].agedelete
在迭代期间删除或新增键值时安全的。 m : make(map[int]int)for i : 0; i 10; i {m[i] i 10}for k : range m {if k 5 {m[100] 1000}delete(m, k)fmt.Println(k, m)}//1 map[0:10 2:12 3:13 4:14 5:15 9:19]//3 map[0:10 2:12 4:14 5:15 9:19]//4 map[0:10 2:12 5:15 9:19]//5 map[0:10 2:12 9:19 100:1000]//0 map[2:12 9:19 100:1000]//2 map[9:19 100:1000]//9 map[100:1000]不能保证迭代操作会删除新增的键值 Go没有为map提供清空所有元素的函数清空map唯一的办法是重新make一个新的map。 Go语言的垃圾回收比写一个清空函数更高效 map是引用类型
map与切片相似都是引用类型。将一个map赋值给一个新的变量时它们指向同一块内存因此修改两个变量的内容都能够引起它们所指向的数据发生变化。 map1 : map[string]string{hello:123,demo:321,}fmt.Println(原始map: ,map1)newMap : map1newMap[hello]456fmt.Println(修改后newMap: ,newMap)fmt.Println(修改后map: ,map1)
// 原始map: map[demo:321 hello:123]
// 修改后newMap: map[demo:321 hello:456]
// 修改后map: map[demo:321 hello:456]并发操作
运行时会对字典并发操作做出检测如果某个任务正在对字段进行写操作那么其他任务就不能对该字典并发操作否则会导致进程崩溃。 m : make(map[string]int)go func() {for {m[a] 1 // 写操作time.Sleep(time.Millisecond)}}()go func() {for {_ m[b] // 读操作time.Sleep(time.Millisecond)}}()select {} // 阻止进程退出// fatal error: concurrent map read and map write