Eli's Blog

1. 指针

指针不是内存地址。

内存地址是内存中每个字节单元的唯一编号,而指针则是一个实体。

指针会分配内存空间,相当于一个专门用来保存地址的整型变量。

GO指针,不支持加减运算和类型转换

可通过unsafe.Pointer将指针转换为uintptr后进行加减法运算,但可能会造成非法访问。

Pointer类似C语言中的void*万能指针,可用来转换指针类型。它能安全持有对象或对象成员,但uintptr不行。后者仅是一种特殊整型,并不引用目标对象,无法阻止垃圾回收器回收对象内存。

1.1 引用传递

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
const MAX int = 3

func main() {
var a int = 10
var b int = 20

fmt.Println(a, b)

swap(&a, &b)

fmt.Println(a, b)
}

func swap(x *int, y *int) {
var temp int
temp = *x
*x = *y
*y = temp
}

1.2 指针类型

1.2.1 三种指针:

  • *T:普通指针,用于传递地址,不能进行指针运算

  • unsafe.Pointor: 通用指针类型。用于转换不同类型的指针,不能进行指针运算

  • uintptr: 用于指针运算。GC不把uintptr当指针,uintptr无法持有对象。uintptr类型的目标会被回收

1.2.2 unsafe.Pointer 作用

  • unsafe.Pointer 可以和 普通指针 进行互相转换
  • unsafe.Pointer 可以和 uintptr 进行相互转换
  • unsafe.Pointer 是桥梁,可以让任意类型的指针实现相互转换,也可以将任意类型的指针转换为uintptr进行指针运算

2. 切片 (Slice)

切片是对数组的抽象。数组长度固定,而切片长度不固定,可追加元素,被称为动态数组。

  • 不是数组,但指向底层的数组
  • 可现实变长数组
  • 为引用类型
  • 可直接创建(make)或从底层数组获取生成
  • len()获取元素个数,cap()获取容量
  • 不支持比较操作(==, >, <)
1
2
3
4
5
type slice struct {
array unsafe.Pointer
len int
cap int
}

2.1 创建切片

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
func main() {
s1 := make([]int, 3, 5)
s2 := make([]int, 3)
s3 := []int{10, 20, 3: 30}

arr := [...]int{1, 2, 3, 4, 5}
s4 := arr[:]
s5 := arr[2:4] // cap=len(arr)-2

fmt.Println(s1, len(s1), cap(s1)) // [0, 0, 0] 3 5
fmt.Println(s2, len(s2), cap(s2)) // [0, 0, 0] 3 3
fmt.Println(s3, len(s3), cap(s3)) // [10, 20, 0, 30] 4 4
fmt.Println(s4, len(s4), cap(s4)) // [1, 2, 3, 4, 5] 5, 5
fmt.Println(s5, len(s5), cap(s5)) // [3, 4] 2 3
}

2.2 空切片

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
func main() {
var s1 []int
s2 := []int{}

fmt.Println(s1==nil, s2==nil) // true false

// &reflect.SliceHeader{Data:0x0, Len:0, Cap:0}
fmt.Printf("a: %#v\n", (*reflect.SliceHeader)(unsafe.Pointer(&s1)))

// &reflect.SliceHeader{Data:0x118efd0, Len:0, Cap:0}
fmt.Printf("b: %#v\n", (*reflect.SliceHeader)(unsafe.Pointer(&s2)))

fmt.Printf("Size of a: %d\n", unsafe.Sizeof(s1)) // 24
fmt.Printf("Size of a: %d\n", unsafe.Sizeof(s2)) // 24
}

2.3 复制数据

允许指向同一底层数组,允许目标区间重叠。最终所复制长度以较短的切片长度(len)为准

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
func main() {
s1 := []int{1, 2, 3, 4, 5, 6}
s2 := []int{7, 8, 9}

copy(s1, s2) // dst, src
fmt.Println(s1) // [7, 8, 9, 4, 5, 6]
fmt.Println(s2) // [7, 8, 9]

s3 := []byte{'a', 'b', 'c'}
s4 := []byte{'d', 'e', 'f', 'g', 'h'}

copy(s3, s4)
fmt.Println(s3) // ['d', 'e', 'f']
fmt.Println(s4) // ['d', 'e', 'f', 'g', 'h']
}

2.4 扩容数据

1
2
3
4
5
6
7
8
9
10
11
12
13
func main() {
s1 := make([]int, 3, 6)
fmt.Printf("%v %p\n", s1, s1)
fmt.Println(len(s1), cap(s1)) // 3, 6

s1 = append(s1, 1, 2, 3)
fmt.Printf("%v %p\n", s1, s1) // 元素个数小于等于cap,地址未发生改变
fmt.Println(len(s1), cap(s1)) // 6, 6

s1 = append(s1, 4)
fmt.Printf("%v %p\n", s1, s1) // 元素个数大于原始cap,重新分配内存(底层数组重构),地址发生改变
fmt.Println(len(s1), cap(s1)) // 7, 12
}

Slice坑:slice虽然是引用,但可能被重新分配内存

1
2
3
4
5
6
7
8
9
10
11
func foo(s []int) {
s = append(s, 1) // 增加的元素个数大于cap-len, 重新分配内存地址
}

func main() {
s := make([]int, 0)
fmt.Println(s)

foo(s)
fmt.Println(s) // []
}

3. 集合 (map)

make([keyType]valueType, cap)

3.1 基本操作

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
func main() {
m := map[string]int {
"a": 1,
"b": 2,
}

m["a"] = 5
m["c"] = 8

if v, ok := m["d"]; ok {
println(v)
}

delete(m, "d")

for k, v := range m {
println(k, ":", v, " ")
}
}

3.2 多层map嵌套

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
func main() {
m := make(map[int]map[int]string)

// initialize sub-map
m[1] = make(map[int]string)
m[1][1] = "OK"

fmt.Println(m)

a, ok := m[2][1] // test if sub-map is initialized
fmt.Println(a, ok)
if !ok {
m[2] = make(map[int]string)
}
fmt.Println(m)
}

3.3 不支持修改成员值(struct或array)

字典被设计成“not addressable”,故不能直接修改value成员(结构或数组)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
func main() {
m := map[int]user{
1: user{"Jack", 23},
2: user{"Tom", 22},
}

//m[1].age++ // cannot assign to struct field in a map

jack := m[1]
jack.age++
m[1] = jack // 必须重新赋值
fmt.Println(m)

// 指针方式
m2 := map[int]*user{
1: &user{"Jack", 23},
2: &user{"Tom", 22},
}

m2[1].age++
fmt.Println(m2[1])
}

type user struct {
name string
age int
}
1
2
3
4
5
6
7
8
9
10
11
12
13
func main() {
m := map[string][2]int {
"a": {1, 2},
}

//s := m["a"][:] // 数组必须addressable,否则会引发错误。

a := m["a"]
fmt.Printf("%p, %v\n", &a, a)

s := a[:]
fmt.Printf("%p, %v\n", &s, s)
}

3.4 map间接排序

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
func main() {
m := map[int]string{2: "b", 5: "e", 1: "a", 3: "c", 4: "d"}
s := make([]int, len(m))

i := 0
for k, _ := range m {
s[i] = k
i++
}

fmt.Println(s)

sort.Ints(s) // 索引排序
fmt.Println(s)

for _, v := range s {
fmt.Println(m[v])
}
}

3.5 并发读写字典

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
func main() {
var lock sync.RWMutex
m := make(map[string]int)

go func() {
for {
lock.Lock()
m["a"] += 1
lock.Unlock()

time.Sleep(time.Microsecond)
}
}()

go func() {
for {
lock.RLock()
_ = m["b"]
lock.RUnlock()

time.Sleep(time.Microsecond)
}
}()

select {}
}

4. range

用于for循环中迭代数组(array)、切片(slice)、通道(channel)或集合(map)的元素。

  • slice(i, v)
  • map(k, v)

4.1 示例:遍历map

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
func main() {
sm := make([]map[int]string, 3)
for _, v := range sm {
v = make(map[int]string) // range中v的值是一份拷贝,无法修改
v[1] = "OK"
fmt.Println(v)
}
fmt.Println(sm)

// 可正确修改
for i := range sm {
sm[i] = make(map[int]string)
sm[i][1] = "OK"
fmt.Println(sm[i])
}
fmt.Println(sm)
}

4.2 示例:遍历slice

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
func main() {
nums := []int {2, 3, 4}
sum := 0

for _, num := range nums {
sum += num
}

fmt.Println("sum:", sum)

for i, num := range nums {
if num == 3 {
fmt.Println("index:", i)
}
}

for i, c := range "go语言" {
fmt.Println(i, c) // c, unicode值
}
}