GoLang之切片底层系列三(深度学习)

GoLang之切片底层系列三(深度学习)

注: 本文代码以Go SDK v1.18测试

1.slice类型基本结构

slice由三个部分组成:
data:元素存哪里
len:存了多少个元素
cap:可以存多少个元素

2.var创建

举个例子,声明一个整型slice:

var ints []int

变量ints的结构如下图所示:
slice的元素要存在一段连续的内存中,实际上就是个数组 ,data就是这个底层数组的起始地址。但目前只分配了这个切片结构,还没有分配底层数组,所以data为nil,存储元素个数len为0,容量cap也为0。

在这里插入图片描述
image-20220305130915916

3.make创建

如果通过make的方式定义这个变量,不仅会分配这三部分结构,还会开辟一段内存作为它的底层数组。

func main() {
	var ints []int = make([]int, 2, 5)
	fmt.Println(ints)//输出:[0 0]
}

这里make会为ints开辟一段容纳5个整型元素的内存,还会把它们初始化为整型的默认值 0。但是目前这个slice变量只存储了两个元素,所以data指向图中位置,len为2,cap为5,所以变量ints的结构如下图所示。

在这里插入图片描述
image-20220305132059488

接下来,添个元素试试~

func main() {
	var ints []int = make([]int, 2, 5)
	fmt.Println(len(ints), cap(ints)) //输出 :2 5
	fmt.Println(ints)//输出:[0 0]
	ints = append(ints, 1)
	fmt.Println(len(ints), cap(ints))//输出: 3 5
	fmt.Println(ints)//输出: [0 0 1]
}

已经存了两个,所以新添加的是第三个,len修改为3。

在这里插入图片描述
image-20220305132502848

ints[0] = 1

已经存储的元素是可以安全读写的,但是超出这个范围就属于越界访问,会发生panic。

在这里插入图片描述
image-20220305132840591

4.new创建

再来个例子,这次我们看看字符串类型的slice,但是不用make,来试试new。

 ps := new([]string)

func main() {
	ps := new([]string)
	if ps == nil {
		fmt.Println("yes")
	} else {
		fmt.Println("no") //no
	}
}

new一个slice变量同样会分配这三部分结构,但它不负责底层数组的分配,所以data=nil,len和cap都是0。new的返回值就是slice结构的起始地址,所以ps它就是个地址。

在这里插入图片描述

此时这个slice变量还没有底层数组,像下面这样的操作是不允许的:

(*ps)[0] = "eggo"

image-20220305133543173

那谁来给它分配底层数组呢?
答案是:append

*ps = append(*ps, "eggo")

通过append的方式添加元素,append就会给它开辟底层数组。如下图所示,这里开辟了一个字符串元素的数组。

在这里插入图片描述
image-20220305134003419

注意其中字符串类型由两部分组成,一个内容起始地址,指向字符串内容,还有一个字节长度。
接下来我们看看和slice密切相关的——底层数组。

5.底层数组

注意其中字符串类型由两部分组成,一个内容起始地址,指向字符串内容,还有一个字节长度。
接下来我们看看和slice密切相关的——底层数组。

arr := [10]int{0,1,2,3,4,5,6,7,8,9}

变量arr是容量为10的整型数组(注意数组容量声明了就不能变了)。我们可以把不同的slice关联到同一个数组,如下代码所示:

var s1 []int = arr[1:4]
var s2 []int = arr[7:]

s1和s2会共用底层数组arr,s1和s2的具体结构如下图所示:
s1的元素是arr索引1到4 左闭右开,所以1,2,3这3个元素算是添加到s1中了。但是容量却是从s1的data这里开始,到底层数组结束共有9个元素。
slice访问和修改的都是底层数组的元素,所以s1[3]就算访问越界了。

在这里插入图片描述

如果修改s1的定义为:

var s1 []int = arr[1:5]

或者通过append添加元素来扩大可读写的区间范围:


s1 = append(s1,4)

这样就可以访问s1[3]了。

再来看s2,s2的元素从索引7开始直到结束,共3个元素,容量也是3。此时
如果再给s2添加元素会怎样?

s2 = append(s2, 10)

在这里插入图片描述

arr这个底层数组是不能用了,得开辟新数组。但是原来的元素要拷过来,还要添加新元素10。元素个数改为4,容量扩到6。这下slice和底层数组的关系都清晰了吧!

不过,还有个问题,我只添加了一个元素,s2怎么从3扩容到6了呢?那就要看slice的扩容规则了~

6.扩容规则(1.15)

扩容规则第一步:预估扩容后的容量。
怎么预估?来看个例子。

ints := []int{1,2}
ints = append(ints, 3, 4, 5)

这里扩容前容量oldCap为2,添加三个元素,那至少得扩容到cap=5吧?难道就预估到5,这么简单粗暴的吗?
当然不是,预估也是有规则的~

oldCap:扩容前容量
oldLen:扩容前元素个数
cap:扩容所需最小容量
newCap:预估容量

Go1.15中,预估容量规则如下:
(1)如果扩容前的容量翻倍之后还是小于所需最小容量,那么预估容量就等于所需最小容量。
(2)如果不满足第一条,而且扩容前容量小于1024,那就直接翻倍没商量。
(3)如果不满足第一条,而且扩容前容量大于等于1024,那就循环扩容四分之一,直到大于等于所需最小容量。
在上面这个例子中,扩容前容量为2,就算翻倍了还是小于5,所以预估容量就是5。

在这里插入图片描述

扩容规则第二步:
预估容量只是预估的元素“个数”,这么多元素需要占用多少内存呢?这就和元素类型挂钩了。
用预估的容量,乘以元素类型大小,得到的就是所需内存。
难道直接分配这么多内存就ok了?并不是。

简单来说,是因为在许多编程语言中,申请分配内存并不是直接与操作系统交涉,而是和语言自身实现的内存管理模块。它会提前向操作系统申请一批内存
分成常用的规格管理起来,我们申请内存时,它会帮我们匹配到足够大、且最接近的规格。
这就是第三步要做的事情:将预估申请内存匹配到合适的内存规格。
在我们的例子中,预估容量为5,64位下就需要申请40字节存放扩容后的底层数组,而实际申请会匹配到48字节。
那这么大的内存能装多少个元素呢?
这个例子每个元素(int)占8字节,一共能装6个,这就是扩容后的容量了。

在这里插入图片描述

7.扩容规则(1.16)

Go1.16中有了些变化,和1024比较的不再是oldLen,而是oldCap。如下代码所示:

// 1.16
newcap := old.cap
doublecap := newcap + newcap
if cap > doublecap {
  newcap = cap
} else {
  if old.cap < 1024 {
    newcap = doublecap
  } else {
    // Check 0 < newcap to detect overflow
    // and prevent an infinite loop.
    for 0 < newcap && newcap < cap {
      newcap += newcap / 4
    }
    // Set newcap to the requested cap when
    // the newcap calculation overflowed.
    if newcap <= 0 {
      newcap = cap
    }
  }
}

8.扩容规则(1.18)

到了Go1.18时,又改成不和1024比较了,而是和256比较;并且扩容的增量也有所变化,不再是每次扩容1/4,如下代码所示:


//1.18
newcap := old.cap
doublecap := newcap + newcap
if cap > doublecap {
  newcap = cap
} else {
  const threshold = 256
  if old.cap < threshold {
    newcap = doublecap
  } else {
    // Check 0 < newcap to detect overflow
    // and prevent an infinite loop.
    for 0 < newcap && newcap < cap {
      // Transition from growing 2x for small slices
      // to growing 1.25x for large slices. This formula
      // gives a smooth-ish transition between the two.
      newcap += (newcap + 3*threshold) / 4
    }
    // Set newcap to the requested cap when
    // the newcap calculation overflowed.
    if newcap <= 0 {
      newcap = cap
    }
  }
}

趁热打铁,再来个例子练练手~

9.小练习

a是string类型的slice,64位下每个元素占16字节,以Go1.16为例。

a := []string{"My", "name", "is"}
a = append(a, "eggo")

第一步:
扩容前容量是3,添加一个元素,最少要扩容到4。
原容量翻倍为6,大于4;
且原容量和1024比,小于1024,所以直接翻倍,预估容量为6,预估容量newcap为6。

第二步:
预估容量乘以元素大小(6*16=96),等于96字节。

第三步:
96字节匹配到内存规格也是96字节。
所以,最终扩容后容量为6。

func main() {
	a := []string{"My", "name", "is"}
	fmt.Println(a, len(a), cap(a)) //[My name is] 3 3
	a = append(a, "eggo")
	fmt.Println(a, len(a), cap(a)) //[My name is eggo] 4 6
}

在这里插入图片描述

关于Go语言的slice就先介绍到这里~

10.内存分配规律

举个现实中的例子来说
你家里有五个人,每个人都想吃绿豆糕,因此你的需求就是 5,对应上例中的 cap ,于是你就到超市里去买。
但超市并不是你家开的,绿豆糕都是整盒整盒的卖,没有卖散的,每盒的数量是 6 个,因此你最少买 6 个。
每次购买的最少数量,就可以类比做 Go 的内存分配规律。
只有了解了 Go 的内存分配规律,我们才能准确的计算出我们最少得买多少的绿豆糕(得申请多少的内存,分配多少的容量)。

关于内存管理模块的代码,在 runtime/sizeclasses.go
从下面这个表格中,可以总结出一些规律:
在小于16字节时,每次以8个字节增加
当大于16小于2^8时,每次以16字节增加(2^8=256)
当大于2^8小于2^9时以32字节增加
依此规律…

// Code generated by mksizeclasses.go; DO NOT EDIT.
//go:generate go run mksizeclasses.go

package runtime

// class  bytes/obj  bytes/span  objects  tail waste  max waste  min align
//     1          8        8192     1024           0     87.50%          8
//     2         16        8192      512           0     43.75%         16
//     3         24        8192      341           8     29.24%          8
//     4         32        8192      256           0     21.88%         32
//     5         48        8192      170          32     31.52%         16
//     6         64        8192      128           0     23.44%         64
//     7         80        8192      102          32     19.07%         16
//     8         96        8192       85          32     15.95%         32
//     9        112        8192       73          16     13.56%         16
//    10        128        8192       64           0     11.72%        128
//    11        144        8192       56         128     11.82%         16
//    12        160        8192       51          32      9.73%         32
//    13        176        8192       46          96      9.59%         16
//    14        192        8192       42         128      9.25%         64
//    15        208        8192       39          80      8.12%         16
//    16        224        8192       36         128      8.15%         32
//    17        240        8192       34          32      6.62%         16
//    18        256        8192       32           0      5.86%        256
//    19        288        8192       28         128     12.16%         32
//    20        320        8192       25         192     11.80%         64
//    21        352        8192       23          96      9.88%         32
//    22        384        8192       21         128      9.51%        128
//    23        416        8192       19         288     10.71%         32
//    24        448        8192       18         128      8.37%         64
//    25        480        8192       17          32      6.82%         32
//    26        512        8192       16           0      6.05%        512
//    27        576        8192       14         128     12.33%         64
//    28        640        8192       12         512     15.48%        128
//    29        704        8192       11         448     13.93%         64
//    30        768        8192       10         512     13.94%        256
//    31        896        8192        9         128     15.52%        128
//    32       1024        8192        8           0     12.40%       1024
//    33       1152        8192        7         128     12.41%        128
//    34       1280        8192        6         512     15.55%        256
//    35       1408       16384       11         896     14.00%        128
//    36       1536        8192        5         512     14.00%        512
//    37       1792       16384        9         256     15.57%        256
//    38       2048        8192        4           0     12.45%       2048
//    39       2304       16384        7         256     12.46%        256
//    40       2688        8192        3         128     15.59%        128
//    41       3072       24576        8           0     12.47%       1024
//    42       3200       16384        5         384      6.22%        128
//    43       3456       24576        7         384      8.83%        128
//    44       4096        8192        2           0     15.60%       4096
//    45       4864       24576        5         256     16.65%        256
//    46       5376       16384        3         256     10.92%        256
//    47       6144       24576        4           0     12.48%       2048
//    48       6528       32768        5         128      6.23%        128
//    49       6784       40960        6         256      4.36%        128
//    50       6912       49152        7         768      3.37%        256
//    51       8192        8192        1           0     15.61%       8192
//    52       9472       57344        6         512     14.28%        256
//    53       9728       49152        5         512      3.64%        512
//    54      10240       40960        4           0      4.99%       2048
//    55      10880       32768        3         128      6.24%        128
//    56      12288       24576        2           0     11.45%       4096
//    57      13568       40960        3         256      9.99%        256
//    58      14336       57344        4           0      5.35%       2048
//    59      16384       16384        1           0     12.49%       8192
//    60      18432       73728        4           0     11.11%       2048
//    61      19072       57344        3         128      3.57%        128
//    62      20480       40960        2           0      6.87%       4096
//    63      21760       65536        3         256      6.25%        256
//    64      24576       24576        1           0     11.45%       8192
//    65      27264       81920        3         128     10.00%        128
//    66      28672       57344        2           0      4.91%       4096
//    67      32768       32768        1           0     12.50%       8192

// alignment  bits  min obj size
//         8     3             8
//        16     4            32
//        32     5           256
//        64     6           512
//       128     7           768
//      4096    12         28672
//      8192    13         32768

const (
	_MaxSmallSize   = 32768
	smallSizeDiv    = 8
	smallSizeMax    = 1024
	largeSizeDiv    = 128
	_NumSizeClasses = 68
	_PageShift      = 13
)

var class_to_size = [_NumSizeClasses]uint16{0, 8, 16, 24, 32, 48, 64, 80, 96, 112, 128, 144, 160, 176, 192, 208, 224, 240, 256, 288, 320, 352, 384, 416, 448, 480, 512, 576, 640, 704, 768, 896, 1024, 1152, 1280, 1408, 1536, 1792, 2048, 2304, 2688, 3072, 3200, 3456, 4096, 4864, 5376, 6144, 6528, 6784, 6912, 8192, 9472, 9728, 10240, 10880, 12288, 13568, 14336, 16384, 18432, 19072, 20480, 21760, 24576, 27264, 28672, 32768}
var class_to_allocnpages = [_NumSizeClasses]uint8{0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 1, 2, 1, 2, 1, 3, 2, 3, 1, 3, 2, 3, 4, 5, 6, 1, 7, 6, 5, 4, 3, 5, 7, 2, 9, 7, 5, 8, 3, 10, 7, 4}
var class_to_divmagic = [_NumSizeClasses]uint32{0, ^uint32(0)/8 + 1, ^uint32(0)/16 + 1, ^uint32(0)/24 + 1, ^uint32(0)/32 + 1, ^uint32(0)/48 + 1, ^uint32(0)/64 + 1, ^uint32(0)/80 + 1, ^uint32(0)/96 + 1, ^uint32(0)/112 + 1, ^uint32(0)/128 + 1, ^uint32(0)/144 + 1, ^uint32(0)/160 + 1, ^uint32(0)/176 + 1, ^uint32(0)/192 + 1, ^uint32(0)/208 + 1, ^uint32(0)/224 + 1, ^uint32(0)/240 + 1, ^uint32(0)/256 + 1, ^uint32(0)/288 + 1, ^uint32(0)/320 + 1, ^uint32(0)/352 + 1, ^uint32(0)/384 + 1, ^uint32(0)/416 + 1, ^uint32(0)/448 + 1, ^uint32(0)/480 + 1, ^uint32(0)/512 + 1, ^uint32(0)/576 + 1, ^uint32(0)/640 + 1, ^uint32(0)/704 + 1, ^uint32(0)/768 + 1, ^uint32(0)/896 + 1, ^uint32(0)/1024 + 1, ^uint32(0)/1152 + 1, ^uint32(0)/1280 + 1, ^uint32(0)/1408 + 1, ^uint32(0)/1536 + 1, ^uint32(0)/1792 + 1, ^uint32(0)/2048 + 1, ^uint32(0)/2304 + 1, ^uint32(0)/2688 + 1, ^uint32(0)/3072 + 1, ^uint32(0)/3200 + 1, ^uint32(0)/3456 + 1, ^uint32(0)/4096 + 1, ^uint32(0)/4864 + 1, ^uint32(0)/5376 + 1, ^uint32(0)/6144 + 1, ^uint32(0)/6528 + 1, ^uint32(0)/6784 + 1, ^uint32(0)/6912 + 1, ^uint32(0)/8192 + 1, ^uint32(0)/9472 + 1, ^uint32(0)/9728 + 1, ^uint32(0)/10240 + 1, ^uint32(0)/10880 + 1, ^uint32(0)/12288 + 1, ^uint32(0)/13568 + 1, ^uint32(0)/14336 + 1, ^uint32(0)/16384 + 1, ^uint32(0)/18432 + 1, ^uint32(0)/19072 + 1, ^uint32(0)/20480 + 1, ^uint32(0)/21760 + 1, ^uint32(0)/24576 + 1, ^uint32(0)/27264 + 1, ^uint32(0)/28672 + 1, ^uint32(0)/32768 + 1}
var size_to_class8 = [smallSizeMax/smallSizeDiv + 1]uint8{0, 1, 2, 3, 4, 5, 5, 6, 6, 7, 7, 8, 8, 9, 9, 10, 10, 11, 11, 12, 12, 13, 13, 14, 14, 15, 15, 16, 16, 17, 17, 18, 18, 19, 19, 19, 19, 20, 20, 20, 20, 21, 21, 21, 21, 22, 22, 22, 22, 23, 23, 23, 23, 24, 24, 24, 24, 25, 25, 25, 25, 26, 26, 26, 26, 27, 27, 27, 27, 27, 27, 27, 27, 28, 28, 28, 28, 28, 28, 28, 28, 29, 29, 29, 29, 29, 29, 29, 29, 30, 30, 30, 30, 30, 30, 30, 30, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32}
var size_to_class128 = [(_MaxSmallSize-smallSizeMax)/largeSizeDiv + 1]uint8{32, 33, 34, 35, 36, 37, 37, 38, 38, 39, 39, 40, 40, 40, 41, 41, 41, 42, 43, 43, 44, 44, 44, 44, 44, 45, 45, 45, 45, 45, 45, 46, 46, 46, 46, 47, 47, 47, 47, 47, 47, 48, 48, 48, 49, 49, 50, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 53, 53, 54, 54, 54, 54, 55, 55, 55, 55, 55, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 58, 58, 58, 58, 58, 58, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 61, 61, 61, 61, 61, 62, 62, 62, 62, 62, 62, 62, 62, 62, 62, 62, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67}

11.底层数组(视频讲解)

数组,就是同种数据的元素一个挨着一个的存储;
int型slice,底层就是int数组;string型底层就是string数组;
但是对于slice的data并不是必须指向数组的开头

image-20220305134319808

image-20220305134449778

对于slice的data并不是必须指向数组的开头的例子如下:变量arr是容量为10的整形数组,数组容量声明后就不会再变了,我们可以把不同的slice关联到同一个数组即var s 1 [int] =arr[1:4],他们会共用底层数组;
s1的元素是1、2、3,这三个元素算是添加到f1中了,但是容量是从data开始到底层数组结束共有9个元素;s2的元素是从索引7开始直到结束,共3个元素;slice访问和修改的都是底层数组的元素
对于s1来说如果再访问s1[3]即arr[4]就算访问越界了

image-20220305143200705

func main() {
	arr := [10]int{0, 1, 2, 3, 4, 5, 6, 7, 8, 9}
	var s1 []int = arr[1:4]
	var s2 []int = arr[7:]
	fmt.Println(s1, len(s1), cap(s1)) //[1 2 3] 3 9
	fmt.Println(s2, len(s2), cap(s2)) //[7 8 9] 3 3
}

可以修改范围,或者通过append添加元素,来扩大可读写的区间范围

image-20220305143342702

//append会把元素改变的,地址并不会改变
func main() {
	arr := [10]int{0, 1, 2, 3, 4, 5, 6, 7, 8, 9}
	fmt.Printf("%p\n", &arr) //0xc0000142d0
	var s1 []int = arr[1:4]
	var s2 []int = arr[7:]
	fmt.Println(s1, len(s1), cap(s1)) //[1 2 3] 3 9
	fmt.Println(s2, len(s2), cap(s2)) //[7 8 9] 3 3
	s1 = append(s1, 100)
	fmt.Printf("%p\n", &arr)//0xc0000142d0     
	fmt.Println(arr)                  //[0 1 2 3 100 5 6 7 8 9]
	fmt.Println(s1, len(s1), cap(s1)) //[1 2 3 100] 4 9
	fmt.Println(s2, len(s2), cap(s2)) //[7 8 9] 3 3
}

此时,如果再给s2添加元素的话,原来的那个底层数组是不能再用了,得开辟新数组,原来的数要拷过来,还要添加新元素,元素个数改为4,容量扩到6

image-20220305143634353

func main() {
	arr := [10]int{0, 1, 2, 3, 4, 5, 6, 7, 8, 9}
	fmt.Printf("%p\n", &arr) //0xc00012e0f0
	var s1 []int = arr[1:4]
	var s2 []int = arr[7:]
	fmt.Println(s1, len(s1), cap(s1)) //[1 2 3] 3 9
	fmt.Println(s2, len(s2), cap(s2)) //[7 8 9] 3 3
	s2 = append(s2, 100)
	fmt.Printf("%p\n", &arr)          // 0xc00012e0f0
	fmt.Println(arr)                  //[0 1 2 3 4 5 6 7 8 9]
	fmt.Println(s1, len(s1), cap(s1)) //[1 2 3] 3 9
	fmt.Println(s2, len(s2), cap(s2)) //[7 8 9 100] 4 6
}

长度与容量如下变化如下演示:

func main() {
	arr := []int{2, 3, 5, 7, 11, 13}
	fmt.Println(arr)
	sli1 := arr[1:4]
	fmt.Println(sli1)      //[3 5 7]
	fmt.Println(len(sli1)) //3
	fmt.Println(cap(sli1)) //5
	sli2 := arr[0:4]
	fmt.Println(sli2)      //[2 3 5 7]
	fmt.Println(len(sli2)) //4
	fmt.Println(cap(sli2)) //6

	sli3 := arr[2:3]
	fmt.Println(sli3)      //[5]
	fmt.Println(len(sli3)) //1
	fmt.Println(cap(sli3)) //4

	sli3[0] = 100
	fmt.Println(sli3)//[100]
	fmt.Println(arr)//[2 3 100 7 11 13]

}

12.扩容规则(1.15)(视频讲解)

Go1.15中,预估容量规则如下:
oldcap:扩容前容量
cap:所需最小容量
newcap :预估容量 ;
oldLen:扩容前元素个数
1.计算预估扩容容量:
如果扩容前容量oldcap翻倍的话,还是小于所需最小容量cap,那么预估容量newcap就等于所需最小容量cap,否则,就要再细分;如果扩容前元素个数oldLen小于1024,那么预估容量newcap就等于扩容前容量oldcap两倍,如果扩容前元素个数oldLen大于等于1024,那么预估容量newcap就先在扩容前容量oldcap的基础上扩四分之一,也就是扩到原来的1.25倍
为何要要按以上的扩容规则呢?:
目的就是为了尽量可以扩容少的容量即可装下装入的元素,扩容前元素个数小于1024时,最少扩容oldcap的两倍

image-20220305160055388

func main() {
	a := []string{"My", "name", "is"}
	fmt.Println(a, len(a), cap(a)) //[My name is] 3 3
	fmt.Printf("%p\n", &a)         //0xc000004078
	fmt.Println(&a[0])             //0xc000110480
	a = append(a, "eggo")
	fmt.Println(a, len(a), cap(a)) //[My name is eggo] 4 6
	fmt.Printf("%p\n", &a)         //0xc000004078
	fmt.Println(&a[0])             //0xc000068060        首地址已经变了
}
func main() {
	ints := []int{1, 2}
	fmt.Println(ints, len(ints), cap(ints)) //[1 2] 2 2
	ints = append(ints, 3)
	fmt.Println(ints, len(ints), cap(ints)) //[1 2 3 ]  3 4
}

2.预估容量newcap只是预估的元素的"个数",这么多元素,需要占多大内存呢?这就和元素类型挂钩了,用预估的容量乘以元素类型大小,得到的就是所需内存,当然并不是直接分配这么多内存就ok了,简单来说是因为申请分配内存并不是直接与操作系统交涉,而是和语言自身的内存管理模块交涉,内存管理模块会提前向操作系统申请一批内存,分成常用的规格管理起来,我们申请内存时,它会帮我们匹配到足够大且最接近的规格,这就是第三步需要做的事,即将预估申请内存匹配到合适的内存规格;

image-20220305153931506

image-20220305153901207

3.在上面的例子中,预估容量newcap为5,64位下就需要申请40字节存放扩容后的底层数组,而实际申请时会匹配到48字节,那这么大的内存能装多少个元素呢?在这个例子中每个元素占8字节,一共能装6个,这就是扩容后的容量

image-20220305160751646

在这里插入图片描述

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

GoGo在努力

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值