先创建一个数组,并获取数组的指针地址与数组元素内对应的指针地址
    array := [5]int{1, 2, 3, 4, 5}
    fmt.Println(array, "array 地址为: ", unsafe.Pointer(&array), "字节数是: ", unsafe.Sizeof(array))
    for i := 0; i < len(array); i++ {
        fmt.Printf("%d %p \n", array[i], &array[i])
    }
打印结果:
我们可以看到通过unsafe.Pointer(&array)获取到的是数组第一个元素对应的指针,并且指针地址是连续的。
[1 2 3 4 5] array 地址为:  0xc00000a390 字节数是:  40
1 0xc00000a390 
2 0xc00000a398 
3 0xc00000a3a0 
4 0xc00000a3a8 
5 0xc00000a3b0

基于array数组新建切片,新建片切有多种方式,可自行了解
//切片容量计算方式为:原始数组总长度-新建切片时的startIndex=切片的容量
sliceArray := array[:]
fmt.Println(sliceArray, "sliceArray 切片的长度是: ", len(sliceArray), "容量是: ", cap(sliceArray), "字节数是: ", unsafe.Sizeof(sliceArray))
// 打印结果: [1 2 3 4 5] sliceArray 切片的长度是: 5 容量是: 5 字节数是: 24

sliceArray := array[1:3]
fmt.Println(sliceArray, "sliceArray 切片的长度是: ", len(sliceArray), "容量是: ", cap(sliceArray), "字节数是: ", unsafe.Sizeof(sliceArray))
// 打印结果: [2 3] sliceArray 切片的长度是: 2 容量是: 4 字节数是: 24
按照正常理解,我们基于原数组array新增的切片数据是[1 2 3 4 5],那么它的字节数应该是 5*8=40才对(64位系统int类型占8位)。

其实是当我们创建切片时,golang会帮我们包一层
SliceHeader结构: 结构体中 Data、Len、Cap 3*8=24所以当我们获取切片的字节是一直是24字节
type SliceHeader struct {
    Data uintptr  //切片数据地址,对应的是原数组第一个元素的地址
    Len  int  //切片的长度
    Cap  int  //切片的容量
}

   

  知道了SliceHeader我们换一种写法,获取切片数据的字节数:

    sliceArray := array[1:3]
    sh := (*reflect.SliceHeader)(unsafe.Pointer(&sliceArray))
    sliceData := *(*[4]int)(unsafe.Pointer(sh.Data))
    fmt.Println(sliceData, "sliceData uintptr: ", sh.Data, " Len: ", sh.Len, " Cap: ", sh.Cap, "Data Byte", unsafe.Sizeof(sliceData))
    //输出结果 [2 3 4 5] sliceData uintptr:  824635089048  Len:  2  Cap:  4 Data Byte 32

   经过分析得知:切片实际占用的内存大小跟容量有关系。容量是多少就占用多少内存,我们平常取切片值时,只不过是通过长度取部分的数据。如代码中sliceArray的长度为2,但实际的占用字节是32字节

 

   接下来再看看当切片长度小于容量时,对切片进行新增元素操作会发生什么

    array := [5]int{1, 2, 3, 4, 5}
    sliceArray := array[1:3]
    sliceArray = append(sliceArray, 6)
    fmt.Println(array)
    // 打印结果: [1 2 3 6 5]

   测试得知:容量大于长度时,对切片的新增,其实就是对原始数组的修改。

 

   接下来再看看当切片长度等于容量时,对切片进行新增元素操作会发生什么

    array := [5]int{1, 2, 3, 4, 5}
    sliceArray := array[:]
    sliceArray = append(sliceArray, 6)
    fmt.Println(array)
    // 打印结果: [1 2 3 4 5]

    sh := (*reflect.SliceHeader)(unsafe.Pointer(&sliceArray))
    sliceData := *(*[6]int)(unsafe.Pointer(sh.Data))
    fmt.Println(sliceData, "sliceData uintptr: ", sh.Data, " Len: ", sh.Len, " Cap: ", sh.Cap, "Data Byte", unsafe.Sizeof(sliceData))
    //输出结果 [1 2 3 4 5 6] sliceData uintptr:  824633770624  Len:  6  Cap:  10 Data Byte 48

    测试得知:当切片容量扩容时,会重新开辟一块内存给扩容后的切片,所作的操作都是基于新的内存地址进行操作。

 


版权声明:本文为HaoKeKe原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://www.cnblogs.com/HaoKeKe/p/13743783.html