这篇文章介绍go语言slice数据类型的定义,内部格式,以及如何声明和定义slice类型变量。
slice数据类型大小
package main
import (
"fmt"
"unsafe"
)
func main() {
var s1 []int64
var s2 []int64 = make([]int64, 0)
var s3 []int64 = make([]int64, 2)
var s4 []int64 = make([]int64, 4)
fmt.Printf("s1=%d,s2=%d,s3=%d,s4=%d\n", unsafe.Sizeof(s1), unsafe.Sizeof(s2), unsafe.Sizeof(s3), unsafe.Sizeof(s4))
}
$ go build && ./main
s1=24,s2=24,s3=24,s4=24
slice数据类型占用24字节,与slice的内容没有关系,因为slice里面存储的是指向真实数据的指针。
slice数据类型的存储格式
前面我们已经看到slice占用空间是24字节,由三个域组成:
type struct {
v1 uintptr // 指向真实的数据buffer指针
v2 uint64 // 标记当前slice的len值
v3 uint64 // 标志当前slice的cap值
}
slice类型变量的定义
- var s1 []int64
声明一个slice变量,这个slice变量的长度为0,所以不能使用s1[index]来索引,否则会引发错误
panic: runtime error: index out of range
但是可以使用s1 = append(s1, 1) - var s2 []int64 = make([]int64, 0)
和前面用法一致,只不过由make执行了初始化工作,初始化工作把s2的v1指针指向一个常量0;而s1没有调用make的初始化,只是执行了变量的初始化,把s1整体初始化成0,从而v1, v2, v3也都是0。 - var s3 []int64 = make([]int64, 2)
创建一个包含两个元素的slice对象,此时可以使用s3[0]和s3[1]对这两个元素进行初始化。
我们看到s1和s2的变量定义中,他们并没有分配存储空间,也就是说他们的大小为零,因此任何使用s1[index]/s2[index]的方式都是导致下标访问越界。唯一能往里面添加数据是只能是使用append函数
append(s1, 11), append(s2, 22)。
代码
package main
import (
"fmt"
"unsafe"
)
type myslice struct {
v1 uintptr
v2 uint64
v3 uint64
}
var p * myslice
func printmemory(p uintptr, size int) {
fmt.Printf("[0x%16x:%2d] =", p, size)
for i := 0; i < size; i++ {
p1 := unsafe.Pointer(p + uintptr(i))
p2 := (*byte)(unsafe.Pointer(p1))
fmt.Printf(" %x", *p2)
}
fmt.Printf("\n")
}
func main() {
var s1 []int64
var s2 []int64 = make([]int64, 0)
var s3 []int64 = make([]int64, 2)
//s1[0] = 0x1234; // panic: runtime error: index out of range
//s2[0] = 0x1234; // panic: runtime error: index out of range
s3[0] = 0x1234;
fmt.Printf("s1=%d,s2=%d,s3=%d\n", len(s1), len(s2), len(s3))
p = (* myslice)(unsafe.Pointer(&s1));
fmt.Printf("p=%p,v1=0x%x,v2=%x,v=%x\n", p, p.v1, p.v2, p.v3)
//printmemory(p.v1, 8)
p = (* myslice)(unsafe.Pointer(&s2));
fmt.Printf("p=%p,v1=0x%x,v2=%x,v=%x\n", p, p.v1, p.v2, p.v3)
printmemory(p.v1, 8)
p = (* myslice)(unsafe.Pointer(&s3));
fmt.Printf("p=%p,v1=0x%x,v2=%x,v=%x\n", p, p.v1, p.v2, p.v3)
printmemory(p.v1, 8)
}
运行结果
$ go build && ./main
s1=0,s2=0,s3=2
p=0xc42000a2a0,v1=0x0,v2=0,v=0
p=0xc42000a2c0,v1=0x517cf8,v2=0,v=0
[0x 517cf8: 8] = 0 0 0 0 0 0 0 0
p=0xc42000a2e0,v1=0xc42000e270,v2=2,v=2
[0x c42000e270: 8] = 34 12 0 0 0 0 0 0
可以看到s1和s2的len值都是0,即他们都没有元素空间;s1和s2唯一不同的是s2的v1字段是有一个值0x517cf8,而s1是没有的,是0值;这个值是go常量,指向0。s3的v1指向slice的数据地址,我们看到第一个元素值是0x1234,那是之前s3[0] = 0x1234设置的。
网友评论