slice 和 string 的底层实现

通过将 string 和 slice 转换为runtime 中的 slice 和 stringStruct 类型 然后 通过 指针获取下标的值。

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
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
package main

import (
"fmt"
"unsafe"
)

// src/runtime/slice.go
type slice struct {
array unsafe.Pointer // 指向数组的指针
len int
cap int
}

// src/runtime/string.go
type stringStruct struct {
str unsafe.Pointer //其实就是byte数组
len int
}

func main() {
///// string ////
str := "hello"
strStruct := (*stringStruct)(unsafe.Pointer(&str))
for i := 0; i < strStruct.len; i++ {
fmt.Printf("%c ", *(*byte)(unsafe.Pointer(uintptr(strStruct.str) + uintptr(i)*unsafe.Sizeof(byte(0))))) // 获取每字节的值
}
fmt.Printf("\n-----------------\n")

//// slice ////
bytes := []byte("Hello, 世界")
bytesSlice := (*slice)(unsafe.Pointer(&bytes))
for i := 0; i < bytesSlice.len; i++ {
fmt.Printf("%d ", *(*byte)(unsafe.Pointer(uintptr(bytesSlice.array) + uintptr(i)*unsafe.Sizeof(byte(0)))))
}
fmt.Printf("\n-----------------\n")

int8s := []int8{0, 9, 8, 7, 6, 90, 89, 87}
int8sSlice := (*slice)(unsafe.Pointer(&int8s))
for i := 0; i < int8sSlice.len; i++ {
fmt.Printf("%d ", *(*int8)(unsafe.Pointer(uintptr(int8sSlice.array) + uintptr(i)*unsafe.Sizeof(int8(0)))))
}
fmt.Printf("\n-----------------\n")

int16s := []int16{0, 9, 8, 7, 6, 90, 89, 87, 800}
int16Slice := (*slice)(unsafe.Pointer(&int16s))
for i := 0; i < int16Slice.len; i++ {
fmt.Printf("%d ", *(*int16)(unsafe.Pointer(uintptr(int16Slice.array) + uintptr(i)*unsafe.Sizeof(int16(0)))))
}
fmt.Printf("\n-----------------\n")

strings := []string{"hello", "世界", "golang"}
stringsSlice := (*slice)(unsafe.Pointer(&strings))
for i := 0; i < stringsSlice.len; i++ {
fmt.Printf("%s ", *(*string)(unsafe.Pointer(uintptr(stringsSlice.array) + uintptr(i)*unsafe.Sizeof(string(""))))) //unsafe.Sizeof(string(""))=16
}
fmt.Printf("\n-----------------\n")
}

输出

1
2
3
4
5
6
7
8
9
10
h  e  l  l  o  
-----------------
72 101 108 108 111 44 32 228 184 150 231 149 140
-----------------
0 9 8 7 6 90 89 87
-----------------
0 9 8 7 6 90 89 87 800
-----------------
hello 世界 golang
-----------------

通过下面的结构也不难看出 string、slice 求 len 的复杂度都是 O(1)
另外这样的定义也给 string 和 []byte 提供了快速的转换方法

1
2
3
4
5
6
7
8
9
10
func StringToBytes(s string) []byte {
x := (*[2]uintptr)(unsafe.Pointer(&s))
h := [3]uintptr{x[0], x[1], x[1]}
return *(*[]byte)(unsafe.Pointer(&h))
}

func BytesToString(b []byte) string {
return *(*string)(unsafe.Pointer(&b))
}

后面 go1.20 官方也出来两个方法,可以很便捷的使用

1
2
3
4
5
6
7
8
9

func StringToBytes(s string) []byte {
return unsafe.Slice(unsafe.StringData(s), len(s))
}


func BytesToString(b []byte) string {
return unsafe.String(unsafe.SliceData(b), len(b))
}

两个方法的定义可以见
String
Slice