直奔主题,我们从源码分析开始,一步一步往下走,慢慢得到我们想知道的内容
slice的源码位于 runtime包
的slice.go文件中,slice结构体
type slice struct {
// go跟c一样是一个强类型的语言,这个unsafe.Pointer 可以看成是C中的(void*)
// 一个(void)类型的指针array,指向了底层的数组结构
array unsafe.Pointer
len int // slice中元素的个数
cap int // 底层数组的容量大小
}
makeslice的源码
提前说一下 uintptr类型的含义,uintptr是指uinptr指针所指内容的大小。通俗的说就是如果指针指向的内容是char类型 uintptr就是一个字节,指向的是一个int类型 uintptr就是4个字节大小。
类似于c语言中的 int *a; sizeof(*a)
func makeslice(et *_type, len, cap int) unsafe.Pointer {
// 计算要分配的堆内存的大小,并返回是否溢出。
// 要分配的堆内存大小=实现slice要用到的底层数组大小cap*一个slice(中数据类型)的大小、
mem, overflow := math.MulUintptr(et.size, uintptr(cap))
// 如果内存溢出,活着要分配内存大于可分配的最大内存,或者slice元素个数比底层实现该slice结构的数组容量还要大
if overflow || mem > maxAlloc || len < 0 || len > cap {
/* 如果没有那么大的内存可分配 或者 cap<len,
那么就分陪len个元素的容量并看一下是否为超过可分配内存限制
*/
mem, overflow := math.MulUintptr(et.size, uintptr(len))
if overflow || mem > maxAlloc || len < 0 {
panicmakeslicelen() // 抛出一个“len out of range”pannic
}
panicmakeslicecap() //抛出一个“cap out of range” panic
}
// 分配内存,这个函数太复杂了,我们暂时不分析
// 返回锁分配的内存的收地址的指针
return mallocgc(mem, et, true)
}
- make()源码
该函数位于src/builtin 包中,由于go内联函数我们看不到函数的实现体,我也不知道为啥看不到,反正是听一位大佬说,内联函数无法看到实现体,可能是代码太烂了,不忍心让人看吧,谁也说不准 😄
func make(t Type, size ...IntegerType) Type
我们就只分析函数名称和返回类型吧。
这个函数提供了两个参数
t
:Type类型(这里的Type只能是map slice 或者chan)
三个点
表示可以有任意多个参数,这些参素的类型是IntegerType类型
返回类型就是t对应的类型。
-----------------------我是分割线------------------------
源码也看了,现在我们分析一下make一个slice的过程,同时意淫以下那个 因为代码太烂而不忍心让大家看到实现体的 make函数到底干了哪些见不得人的勾当。
创建一个slice var s []int = make([]int,5,10)
- 第一个参数t 是一个int类型的数组,那第二个和第三个又是什么意思呢。
- 我们猜测(其实就是)内敛函数make其实调用了
makeslice
,
3.通过前面的makesslice源码分析,第二个参数5其实对应的slice结构体的len,第三个参数10对应的就是slice结构体的cap
4 在make函数中 我们初始化了slice 结构体中的这两个值,同时makesslice返回的指针初始化了slice结构体的array
看到这里,大家应该也能明白为啥slice只能由make创建,因为只有make中能初始化slice结构体中的这两个变量😄
网友评论