教程集 www.jiaochengji.com
教程集 >  Golang编程  >  golang教程  >  正文 简单说说go语言Slice的底层实现

简单说说go语言Slice的底层实现

发布时间:2022-03-07   编辑:jiaochengji.com
教程集为您提供简单说说go语言Slice的底层实现等资源,欢迎您收藏本站,我们将为您提供最新的简单说说go语言Slice的底层实现资源
<svg xmlns="http://www.w3.org/2000/svg" style="display: none;"><path stroke-linecap="round" d="M5,0 0,2.5 5,5z" id="raphael-marker-block" style="-webkit-tap-highlight-color: rgba(0, 0, 0, 0);"/></svg><h1>go语言的Slice</h1> <h2>slice的结构</h2> <pre><code class="lang-go hljs"><span class="token keyword">type</span> slice <span class="token keyword">struct</span> <span class="token punctuation">{</span> array unsafe<span class="token punctuation">.</span>Pointer <span class="token comment">// 指向数组的指针</span> <span class="token builtin">len</span> <span class="token builtin">int</span> <span class="token comment">// 切片长度</span> <span class="token builtin">cap</span> <span class="token builtin">int</span> <span class="token comment">// 切片容量</span> <span class="token punctuation">}</span> </code></pre>

切片我的理解是,他是数组的一个片段,就是说他是数组上截取的一部分,它可以通过下标访问到具体的元素,也能在原数组中<code>自由活动</code>,当你往里面<code>append</code>新元素的时候,如果当前数组容量足够,还会修改原数组的值;如果容量不够了,它就会把原数组中的值拷贝到一个新的数组中(新数组的容量按扩容规则增加),然后新加的元素加入到新的数组中,此时切片的底层数组就不再是原来的数组了,这个时候,你修改切片中的值,原数组就不会发生变化,因为他们不再有关联了,只是对于使用者来说,我们察觉不到这个过程.

<h2>
slice的创建</h2>

下面我们用几个普通的例子来试试创建<code>slice</code>

<pre><code class="lang-go hljs"><span class="token keyword">func</span> <span class="token function">createSlice</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> s <span class="token operator">:=</span> <span class="token function">make</span><span class="token punctuation">(</span><span class="token punctuation">[</span><span class="token punctuation">]</span><span class="token builtin">int</span><span class="token punctuation">,</span> <span class="token number">0</span><span class="token punctuation">)</span> s <span class="token operator">=</span> <span class="token function">append</span><span class="token punctuation">(</span>s<span class="token punctuation">,</span> <span class="token number">1</span><span class="token punctuation">)</span> fmt<span class="token punctuation">.</span><span class="token function">Println</span><span class="token punctuation">(</span>s<span class="token punctuation">)</span> <span class="token keyword">var</span> s2 <span class="token punctuation">[</span><span class="token punctuation">]</span><span class="token builtin">int</span> s2 <span class="token operator">=</span> <span class="token function">append</span><span class="token punctuation">(</span>s2<span class="token punctuation">,</span> <span class="token number">1</span><span class="token punctuation">)</span> fmt<span class="token punctuation">.</span><span class="token function">Println</span><span class="token punctuation">(</span>s2<span class="token punctuation">)</span> s3 <span class="token operator">:=</span> <span class="token punctuation">[</span><span class="token punctuation">]</span><span class="token builtin">int</span><span class="token punctuation">{</span><span class="token punctuation">}</span> s3 <span class="token operator">=</span> <span class="token function">append</span><span class="token punctuation">(</span>s3<span class="token punctuation">,</span> <span class="token number">1</span><span class="token punctuation">)</span> fmt<span class="token punctuation">.</span><span class="token function">Println</span><span class="token punctuation">(</span>s3<span class="token punctuation">)</span> <span class="token punctuation">}</span> <span class="token comment">/** 输出结果为: [1] [1] [1] */</span> </code></pre>

上面的例子还是很好理解的,那我们接下来看看这几种创建模式的差异:

<code>go tool compile -S main.go</code>使用上述命令,查看上面的代码在底层都做了些什么?

完整的返回我这边就不展示了,就展示关键的几行代码:

<pre><code> 0x0021 00033 (main.go:6) LEAQ type.int(SB), AX 0x0028 00040 (main.go:6) PCDATA $0, $0 0x0028 00040 (main.go:6) MOVQ AX, (SP) 0x002c 00044 (main.go:6) XORPS X0, X0 0x002f 00047 (main.go:6) MOVUPS X0, 8(SP) s := make([]int, 0) 0x0034 00052 (main.go:6) CALL runtime.makeslice(SB) ...... 0x003e 00062 (main.go:7) LEAQ type.int(SB), CX ...... s = append(s, 1) 0x005f 00095 (main.go:7) CALL runtime.growslice(SB) ...... 0x008c 00140 (main.go:8) CALL runtime.convTslice(SB) 这个为fmt.Println的具体实现,这里就不多说了 ============================================================ 0x00e7 00231 (main.go:11) LEAQ type.int(SB), AX ...... var s2 []int 0x019f 00415 (main.go:15) LEAQ runtime.zerobase(SB), AX ...... s2 = append(s2, 1) 0x010c 00268 (main.go:11) CALL runtime.growslice(SB) ...... 0x0139 00313 (main.go:12) CALL runtime.convTslice(SB) </code></pre>

从上面我们不难看出,不同的切片创建方法,底层做了不同的操作第一个是<code>runtime.makeslice</code>,第二个没有对应的实现,第三个是<code>runtime.zerobase</code>,这三种方式区别还是有的,但是最终都可以实现我们的目标,因为最终都是调用了<code>runtime.mallocgc</code> 函数,也就是分配内存,看到这有同学就会问了,第二种方式我们并没有对应的实现啊,也就是说并没有分配内存啊。是的,第二种方式我们确实没有直接分配内存,并且我们直接<code>append</code>了,其实是因为<code>append</code>操作会对没有分配内存的切片再分配一下内存(这个我们下面会说到)。所以我们在写代码时,使用 <code>var</code> 关键字声明切片是完全可以的,并且对于长度为0的切片我们非常建议这样声明。

现在我们先看下<code>runtime.makeslice</code>:

<pre><code class="lang-go hljs"><span class="token comment">// et为元素类型</span> <span class="token keyword">func</span> <span class="token function">makeslice</span><span class="token punctuation">(</span>et <span class="token operator">*</span>_type<span class="token punctuation">,</span> <span class="token builtin">len</span><span class="token punctuation">,</span> <span class="token builtin">cap</span> <span class="token builtin">int</span><span class="token punctuation">)</span> unsafe<span class="token punctuation">.</span>Pointer <span class="token punctuation">{</span> mem<span class="token punctuation">,</span> overflow <span class="token operator">:=</span> math<span class="token punctuation">.</span><span class="token function">MulUintptr</span><span class="token punctuation">(</span>et<span class="token punctuation">.</span>size<span class="token punctuation">,</span> <span class="token function">uintptr</span><span class="token punctuation">(</span><span class="token builtin">cap</span><span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token keyword">if</span> overflow <span class="token operator">||</span> mem <span class="token operator">></span> maxAlloc <span class="token operator">||</span> <span class="token builtin">len</span> <span class="token operator"><</span> <span class="token number">0</span> <span class="token operator">||</span> <span class="token builtin">len</span> <span class="token operator">></span> <span class="token builtin">cap</span> <span class="token punctuation">{</span> <span class="token comment">// NOTE: Produce a 'len out of range' error instead of a</span> <span class="token comment">// 'cap out of range' error when someone does make([]T, bignumber).</span> <span class="token comment">// 'cap out of range' is true too, but since the cap is only being</span> <span class="token comment">// supplied implicitly, saying len is clearer.</span> <span class="token comment">// See golang.org/issue/4085.</span> mem<span class="token punctuation">,</span> overflow <span class="token operator">:=</span> math<span class="token punctuation">.</span><span class="token function">MulUintptr</span><span class="token punctuation">(</span>et<span class="token punctuation">.</span>size<span class="token punctuation">,</span> <span class="token function">uintptr</span><span class="token punctuation">(</span><span class="token builtin">len</span><span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token keyword">if</span> overflow <span class="token operator">||</span> mem <span class="token operator">></span> maxAlloc <span class="token operator">||</span> <span class="token builtin">len</span> <span class="token operator"><</span> <span class="token number">0</span> <span class="token punctuation">{</span> <span class="token function">panicmakeslicelen</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">}</span> <span class="token function">panicmakeslicecap</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">}</span> <span class="token keyword">return</span> <span class="token function">mallocgc</span><span class="token punctuation">(</span>mem<span class="token punctuation">,</span> et<span class="token punctuation">,</span> <span class="token boolean">true</span><span class="token punctuation">)</span> <span class="token punctuation">}</span> <span class="token comment">// 首先程序会根据你元素的大小以及容量做个计算(mem=size*cap)以及判断是否溢出,如果溢出了,或者大于了最大可分配的内存大小(与操作系统位数有关),以及长度小于0或者长度小于容量,就会报错.然后下面会再去判断具体是长度错误还是容量错误</span> <span class="token comment">// return mallocgc(mem, et, true) 才是真正去分配内存</span> </code></pre> <h2>
slice的扩容</h2>

网上关于切片的扩容都是什么切片容量小于1024的时候,新切片的容量翻倍,大于1024之后,新切片的容量为旧容量的1.25倍,但是实际上真的是这样的吗?我们先看一段代码:

<pre><code class="lang-go hljs">slice <span class="token operator">:=</span> <span class="token function">make</span><span class="token punctuation">(</span><span class="token punctuation">[</span><span class="token punctuation">]</span><span class="token builtin">int32</span><span class="token punctuation">,</span><span class="token number">3</span><span class="token punctuation">,</span><span class="token number">3</span><span class="token punctuation">)</span> fmt<span class="token punctuation">.</span><span class="token function">Println</span><span class="token punctuation">(</span><span class="token string">"len:"</span><span class="token punctuation">,</span> <span class="token function">len</span><span class="token punctuation">(</span>slice<span class="token punctuation">)</span><span class="token punctuation">,</span> <span class="token string">"cap:"</span><span class="token punctuation">,</span> <span class="token function">cap</span><span class="token punctuation">(</span>slice<span class="token punctuation">)</span><span class="token punctuation">)</span> slice <span class="token operator">=</span> <span class="token function">append</span><span class="token punctuation">(</span>slice<span class="token punctuation">,</span> <span class="token number">1</span><span class="token punctuation">)</span> fmt<span class="token punctuation">.</span><span class="token function">Println</span><span class="token punctuation">(</span><span class="token string">"len:"</span><span class="token punctuation">,</span> <span class="token function">len</span><span class="token punctuation">(</span>slice<span class="token punctuation">)</span><span class="token punctuation">,</span> <span class="token string">"cap:"</span><span class="token punctuation">,</span> <span class="token function">cap</span><span class="token punctuation">(</span>slice<span class="token punctuation">)</span><span class="token punctuation">)</span> fmt<span class="token punctuation">.</span><span class="token function">Println</span><span class="token punctuation">(</span>slice<span class="token punctuation">)</span> <span class="token comment">/** 输出结果如下: len: 3 cap: 3 len: 4 cap: 8 [0 0 0 1] / </span></code></pre>

从上面的例子看好像并不是翻倍耶(我的同事说因为3 1=4 然后翻倍就变成了8,哈哈哈开个玩笑哈),那么这是怎么一回事呢?我们看看源码:

<pre><code class="lang-go hljs"><span class="token keyword">func</span> <span class="token function">growslice</span><span class="token punctuation">(</span>et <span class="token operator">*</span>_type<span class="token punctuation">,</span> old slice<span class="token punctuation">,</span> <span class="token builtin">cap</span> <span class="token builtin">int</span><span class="token punctuation">)</span> slice <span class="token punctuation">{</span> <span class="token keyword">if</span> raceenabled <span class="token punctuation">{</span> callerpc <span class="token operator">:=</span> <span class="token function">getcallerpc</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token function">racereadrangepc</span><span class="token punctuation">(</span>old<span class="token punctuation">.</span>array<span class="token punctuation">,</span> <span class="token function">uintptr</span><span class="token punctuation">(</span>old<span class="token punctuation">.</span><span class="token builtin">len</span><span class="token operator">*</span><span class="token function">int</span><span class="token punctuation">(</span>et<span class="token punctuation">.</span>size<span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">,</span> callerpc<span class="token punctuation">,</span> <span class="token function">funcPC</span><span class="token punctuation">(</span>growslice<span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token punctuation">}</span> <span class="token keyword">if</span> msanenabled <span class="token punctuation">{</span> <span class="token function">msanread</span><span class="token punctuation">(</span>old<span class="token punctuation">.</span>array<span class="token punctuation">,</span> <span class="token function">uintptr</span><span class="token punctuation">(</span>old<span class="token punctuation">.</span><span class="token builtin">len</span><span class="token operator">*</span><span class="token function">int</span><span class="token punctuation">(</span>et<span class="token punctuation">.</span>size<span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token punctuation">}</span> <span class="token keyword">if</span> <span class="token builtin">cap</span> <span class="token operator"><</span> old<span class="token punctuation">.</span><span class="token builtin">cap</span> <span class="token punctuation">{</span> <span class="token function">panic</span><span class="token punctuation">(</span><span class="token function">errorString</span><span class="token punctuation">(</span><span class="token string">"growslice: cap out of range"</span><span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token punctuation">}</span> <span class="token keyword">if</span> et<span class="token punctuation">.</span>size <span class="token operator">==</span> <span class="token number">0</span> <span class="token punctuation">{</span> <span class="token comment">// append should not create a slice with nil pointer but non-zero len.</span> <span class="token comment">// We assume that append doesn't need to preserve old.array in this case.</span> <span class="token keyword">return</span> slice<span class="token punctuation">{</span>unsafe<span class="token punctuation">.</span><span class="token function">Pointer</span><span class="token punctuation">(</span><span class="token operator">&</span>zerobase<span class="token punctuation">)</span><span class="token punctuation">,</span> old<span class="token punctuation">.</span><span class="token builtin">len</span><span class="token punctuation">,</span> <span class="token builtin">cap</span><span class="token punctuation">}</span> <span class="token punctuation">}</span> newcap <span class="token operator">:=</span> old<span class="token punctuation">.</span><span class="token builtin">cap</span> doublecap <span class="token operator">:=</span> newcap <span class="token operator"> </span> newcap <span class="token keyword">if</span> <span class="token builtin">cap</span> <span class="token operator">></span> doublecap <span class="token punctuation">{</span> newcap <span class="token operator">=</span> <span class="token builtin">cap</span> <span class="token punctuation">}</span> <span class="token keyword">else</span> <span class="token punctuation">{</span> <span class="token keyword">if</span> old<span class="token punctuation">.</span><span class="token builtin">len</span> <span class="token operator"><</span> <span class="token number">1024</span> <span class="token punctuation">{</span> newcap <span class="token operator">=</span> doublecap <span class="token punctuation">}</span> <span class="token keyword">else</span> <span class="token punctuation">{</span> <span class="token comment">// Check 0 < newcap to detect overflow</span> <span class="token comment">// and prevent an infinite loop.</span> <span class="token keyword">for</span> <span class="token number">0</span> <span class="token operator"><</span> newcap <span class="token operator">&&</span> newcap <span class="token operator"><</span> <span class="token builtin">cap</span> <span class="token punctuation">{</span> newcap <span class="token operator"> =</span> newcap <span class="token operator">/</span> <span class="token number">4</span> <span class="token punctuation">}</span> <span class="token comment">// Set newcap to the requested cap when</span> <span class="token comment">// the newcap calculation overflowed.</span> <span class="token keyword">if</span> newcap <span class="token operator"><=</span> <span class="token number">0</span> <span class="token punctuation">{</span> newcap <span class="token operator">=</span> <span class="token builtin">cap</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span> <span class="token keyword">var</span> overflow <span class="token builtin">bool</span> <span class="token keyword">var</span> lenmem<span class="token punctuation">,</span> newlenmem<span class="token punctuation">,</span> capmem <span class="token builtin">uintptr</span> <span class="token comment">// Specialize for common values of et.size.</span> <span class="token comment">// For 1 we don't need any division/multiplication.</span> <span class="token comment">// For sys.PtrSize, compiler will optimize division/multiplication into a shift by a constant.</span> <span class="token comment">// For powers of 2, use a variable shift.</span> <span class="token keyword">switch</span> <span class="token punctuation">{</span> <span class="token keyword">case</span> et<span class="token punctuation">.</span>size <span class="token operator">==</span> <span class="token number">1</span><span class="token punctuation">:</span> lenmem <span class="token operator">=</span> <span class="token function">uintptr</span><span class="token punctuation">(</span>old<span class="token punctuation">.</span><span class="token builtin">len</span><span class="token punctuation">)</span> newlenmem <span class="token operator">=</span> <span class="token function">uintptr</span><span class="token punctuation">(</span><span class="token builtin">cap</span><span class="token punctuation">)</span> capmem <span class="token operator">=</span> <span class="token function">roundupsize</span><span class="token punctuation">(</span><span class="token function">uintptr</span><span class="token punctuation">(</span>newcap<span class="token punctuation">)</span><span class="token punctuation">)</span> overflow <span class="token operator">=</span> <span class="token function">uintptr</span><span class="token punctuation">(</span>newcap<span class="token punctuation">)</span> <span class="token operator">></span> maxAlloc newcap <span class="token operator">=</span> <span class="token function">int</span><span class="token punctuation">(</span>capmem<span class="token punctuation">)</span> <span class="token keyword">case</span> et<span class="token punctuation">.</span>size <span class="token operator">==</span> sys<span class="token punctuation">.</span>PtrSize<span class="token punctuation">:</span> lenmem <span class="token operator">=</span> <span class="token function">uintptr</span><span class="token punctuation">(</span>old<span class="token punctuation">.</span><span class="token builtin">len</span><span class="token punctuation">)</span> <span class="token operator">*</span> sys<span class="token punctuation">.</span>PtrSize newlenmem <span class="token operator">=</span> <span class="token function">uintptr</span><span class="token punctuation">(</span><span class="token builtin">cap</span><span class="token punctuation">)</span> <span class="token operator">*</span> sys<span class="token punctuation">.</span>PtrSize capmem <span class="token operator">=</span> <span class="token function">roundupsize</span><span class="token punctuation">(</span><span class="token function">uintptr</span><span class="token punctuation">(</span>newcap<span class="token punctuation">)</span> <span class="token operator">*</span> sys<span class="token punctuation">.</span>PtrSize<span class="token punctuation">)</span> overflow <span class="token operator">=</span> <span class="token function">uintptr</span><span class="token punctuation">(</span>newcap<span class="token punctuation">)</span> <span class="token operator">></span> maxAlloc<span class="token operator">/</span>sys<span class="token punctuation">.</span>PtrSize newcap <span class="token operator">=</span> <span class="token function">int</span><span class="token punctuation">(</span>capmem <span class="token operator">/</span> sys<span class="token punctuation">.</span>PtrSize<span class="token punctuation">)</span> <span class="token keyword">case</span> <span class="token function">isPowerOfTwo</span><span class="token punctuation">(</span>et<span class="token punctuation">.</span>size<span class="token punctuation">)</span><span class="token punctuation">:</span> <span class="token keyword">var</span> shift <span class="token builtin">uintptr</span> <span class="token keyword">if</span> sys<span class="token punctuation">.</span>PtrSize <span class="token operator">==</span> <span class="token number">8</span> <span class="token punctuation">{</span> <span class="token comment">// Mask shift for better code generation.</span> shift <span class="token operator">=</span> <span class="token function">uintptr</span><span class="token punctuation">(</span>sys<span class="token punctuation">.</span><span class="token function">Ctz64</span><span class="token punctuation">(</span><span class="token function">uint64</span><span class="token punctuation">(</span>et<span class="token punctuation">.</span>size<span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token operator">&</span> <span class="token number">63</span> <span class="token punctuation">}</span> <span class="token keyword">else</span> <span class="token punctuation">{</span> shift <span class="token operator">=</span> <span class="token function">uintptr</span><span class="token punctuation">(</span>sys<span class="token punctuation">.</span><span class="token function">Ctz32</span><span class="token punctuation">(</span><span class="token function">uint32</span><span class="token punctuation">(</span>et<span class="token punctuation">.</span>size<span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token operator">&</span> <span class="token number">31</span> <span class="token punctuation">}</span> lenmem <span class="token operator">=</span> <span class="token function">uintptr</span><span class="token punctuation">(</span>old<span class="token punctuation">.</span><span class="token builtin">len</span><span class="token punctuation">)</span> <span class="token operator"><<</span> shift newlenmem <span class="token operator">=</span> <span class="token function">uintptr</span><span class="token punctuation">(</span><span class="token builtin">cap</span><span class="token punctuation">)</span> <span class="token operator"><<</span> shift capmem <span class="token operator">=</span> <span class="token function">roundupsize</span><span class="token punctuation">(</span><span class="token function">uintptr</span><span class="token punctuation">(</span>newcap<span class="token punctuation">)</span> <span class="token operator"><<</span> shift<span class="token punctuation">)</span> overflow <span class="token operator">=</span> <span class="token function">uintptr</span><span class="token punctuation">(</span>newcap<span class="token punctuation">)</span> <span class="token operator">></span> <span class="token punctuation">(</span>maxAlloc <span class="token operator">>></span> shift<span class="token punctuation">)</span> newcap <span class="token operator">=</span> <span class="token function">int</span><span class="token punctuation">(</span>capmem <span class="token operator">>></span> shift<span class="token punctuation">)</span> <span class="token keyword">default</span><span class="token punctuation">:</span> lenmem <span class="token operator">=</span> <span class="token function">uintptr</span><span class="token punctuation">(</span>old<span class="token punctuation">.</span><span class="token builtin">len</span><span class="token punctuation">)</span> <span class="token operator">*</span> et<span class="token punctuation">.</span>size newlenmem <span class="token operator">=</span> <span class="token function">uintptr</span><span class="token punctuation">(</span><span class="token builtin">cap</span><span class="token punctuation">)</span> <span class="token operator">*</span> et<span class="token punctuation">.</span>size capmem<span class="token punctuation">,</span> overflow <span class="token operator">=</span> math<span class="token punctuation">.</span><span class="token function">MulUintptr</span><span class="token punctuation">(</span>et<span class="token punctuation">.</span>size<span class="token punctuation">,</span> <span class="token function">uintptr</span><span class="token punctuation">(</span>newcap<span class="token punctuation">)</span><span class="token punctuation">)</span> capmem <span class="token operator">=</span> <span class="token function">roundupsize</span><span class="token punctuation">(</span>capmem<span class="token punctuation">)</span> newcap <span class="token operator">=</span> <span class="token function">int</span><span class="token punctuation">(</span>capmem <span class="token operator">/</span> et<span class="token punctuation">.</span>size<span class="token punctuation">)</span> <span class="token punctuation">}</span> <span class="token comment">// The check of overflow in addition to capmem > maxAlloc is needed</span> <span class="token comment">// to prevent an overflow which can be used to trigger a segfault</span> <span class="token comment">// on 32bit architectures with this example program:</span> <span class="token comment">//</span> <span class="token comment">// type T [1<<27 1]int64</span> <span class="token comment">//</span> <span class="token comment">// var d T</span> <span class="token comment">// var s []T</span> <span class="token comment">//</span> <span class="token comment">// func main() {</span> <span class="token comment">// s = append(s, d, d, d, d)</span> <span class="token comment">// print(len(s), "\n")</span> <span class="token comment">// }</span> <span class="token keyword">if</span> overflow <span class="token operator">||</span> capmem <span class="token operator">></span> maxAlloc <span class="token punctuation">{</span> <span class="token function">panic</span><span class="token punctuation">(</span><span class="token function">errorString</span><span class="token punctuation">(</span><span class="token string">"growslice: cap out of range"</span><span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token punctuation">}</span> <span class="token keyword">var</span> p unsafe<span class="token punctuation">.</span>Pointer <span class="token keyword">if</span> et<span class="token punctuation">.</span>ptrdata <span class="token operator">==</span> <span class="token number">0</span> <span class="token punctuation">{</span> p <span class="token operator">=</span> <span class="token function">mallocgc</span><span class="token punctuation">(</span>capmem<span class="token punctuation">,</span> <span class="token boolean">nil</span><span class="token punctuation">,</span> <span class="token boolean">false</span><span class="token punctuation">)</span> <span class="token comment">// The append() that calls growslice is going to overwrite from old.len to cap (which will be the new length).</span> <span class="token comment">// Only clear the part that will not be overwritten.</span> <span class="token function">memclrNoHeapPointers</span><span class="token punctuation">(</span><span class="token function">add</span><span class="token punctuation">(</span>p<span class="token punctuation">,</span> newlenmem<span class="token punctuation">)</span><span class="token punctuation">,</span> capmem<span class="token operator">-</span>newlenmem<span class="token punctuation">)</span> <span class="token punctuation">}</span> <span class="token keyword">else</span> <span class="token punctuation">{</span> <span class="token comment">// Note: can't use rawmem (which avoids zeroing of memory), because then GC can scan uninitialized memory.</span> p <span class="token operator">=</span> <span class="token function">mallocgc</span><span class="token punctuation">(</span>capmem<span class="token punctuation">,</span> et<span class="token punctuation">,</span> <span class="token boolean">true</span><span class="token punctuation">)</span> <span class="token keyword">if</span> lenmem <span class="token operator">></span> <span class="token number">0</span> <span class="token operator">&&</span> writeBarrier<span class="token punctuation">.</span>enabled <span class="token punctuation">{</span> <span class="token comment">// Only shade the pointers in old.array since we know the destination slice p</span> <span class="token comment">// only contains nil pointers because it has been cleared during alloc.</span> <span class="token function">bulkBarrierPreWriteSrcOnly</span><span class="token punctuation">(</span><span class="token function">uintptr</span><span class="token punctuation">(</span>p<span class="token punctuation">)</span><span class="token punctuation">,</span> <span class="token function">uintptr</span><span class="token punctuation">(</span>old<span class="token punctuation">.</span>array<span class="token punctuation">)</span><span class="token punctuation">,</span> lenmem<span class="token punctuation">)</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span> <span class="token function">memmove</span><span class="token punctuation">(</span>p<span class="token punctuation">,</span> old<span class="token punctuation">.</span>array<span class="token punctuation">,</span> lenmem<span class="token punctuation">)</span> <span class="token keyword">return</span> slice<span class="token punctuation">{</span>p<span class="token punctuation">,</span> old<span class="token punctuation">.</span><span class="token builtin">len</span><span class="token punctuation">,</span> newcap<span class="token punctuation">}</span> <span class="token punctuation">}</span> </code></pre>

从这里我们可以看出好像的确是按网上说的规则进行的扩容:

<pre><code class="lang-go hljs">newcap <span class="token operator">:=</span> old<span class="token punctuation">.</span><span class="token builtin">cap</span> doublecap <span class="token operator">:=</span> newcap <span class="token operator"> </span> newcap <span class="token keyword">if</span> <span class="token builtin">cap</span> <span class="token operator">></span> doublecap <span class="token punctuation">{</span> newcap <span class="token operator">=</span> <span class="token builtin">cap</span> <span class="token punctuation">}</span> <span class="token keyword">else</span> <span class="token punctuation">{</span> <span class="token keyword">if</span> old<span class="token punctuation">.</span><span class="token builtin">len</span> <span class="token operator"><</span> <span class="token number">1024</span> <span class="token punctuation">{</span> newcap <span class="token operator">=</span> doublecap <span class="token punctuation">}</span> <span class="token keyword">else</span> <span class="token punctuation">{</span> <span class="token comment">// Check 0 < newcap to detect overflow</span> <span class="token comment">// and prevent an infinite loop.</span> <span class="token keyword">for</span> <span class="token number">0</span> <span class="token operator"><</span> newcap <span class="token operator">&&</span> newcap <span class="token operator"><</span> <span class="token builtin">cap</span> <span class="token punctuation">{</span> newcap <span class="token operator"> =</span> newcap <span class="token operator">/</span> <span class="token number">4</span> <span class="token punctuation">}</span> <span class="token comment">// Set newcap to the requested cap when</span> <span class="token comment">// the newcap calculation overflowed.</span> <span class="token keyword">if</span> newcap <span class="token operator"><=</span> <span class="token number">0</span> <span class="token punctuation">{</span> newcap <span class="token operator">=</span> <span class="token builtin">cap</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span> </code></pre>

在这里我们拿到了新的cap应该是6才对是吧,但是为什么就变成了8呢?我们继续往下看:

<pre><code class="lang-go hljs"><span class="token keyword">switch</span> <span class="token punctuation">{</span> <span class="token keyword">case</span> et<span class="token punctuation">.</span>size <span class="token operator">==</span> <span class="token number">1</span><span class="token punctuation">:</span> lenmem <span class="token operator">=</span> <span class="token function">uintptr</span><span class="token punctuation">(</span>old<span class="token punctuation">.</span><span class="token builtin">len</span><span class="token punctuation">)</span> newlenmem <span class="token operator">=</span> <span class="token function">uintptr</span><span class="token punctuation">(</span><span class="token builtin">cap</span><span class="token punctuation">)</span> capmem <span class="token operator">=</span> <span class="token function">roundupsize</span><span class="token punctuation">(</span><span class="token function">uintptr</span><span class="token punctuation">(</span>newcap<span class="token punctuation">)</span><span class="token punctuation">)</span> overflow <span class="token operator">=</span> <span class="token function">uintptr</span><span class="token punctuation">(</span>newcap<span class="token punctuation">)</span> <span class="token operator">></span> maxAlloc newcap <span class="token operator">=</span> <span class="token function">int</span><span class="token punctuation">(</span>capmem<span class="token punctuation">)</span> <span class="token keyword">case</span> et<span class="token punctuation">.</span>size <span class="token operator">==</span> sys<span class="token punctuation">.</span>PtrSize<span class="token punctuation">:</span> lenmem <span class="token operator">=</span> <span class="token function">uintptr</span><span class="token punctuation">(</span>old<span class="token punctuation">.</span><span class="token builtin">len</span><span class="token punctuation">)</span> <span class="token operator">*</span> sys<span class="token punctuation">.</span>PtrSize newlenmem <span class="token operator">=</span> <span class="token function">uintptr</span><span class="token punctuation">(</span><span class="token builtin">cap</span><span class="token punctuation">)</span> <span class="token operator">*</span> sys<span class="token punctuation">.</span>PtrSize capmem <span class="token operator">=</span> <span class="token function">roundupsize</span><span class="token punctuation">(</span><span class="token function">uintptr</span><span class="token punctuation">(</span>newcap<span class="token punctuation">)</span> <span class="token operator">*</span> sys<span class="token punctuation">.</span>PtrSize<span class="token punctuation">)</span> overflow <span class="token operator">=</span> <span class="token function">uintptr</span><span class="token punctuation">(</span>newcap<span class="token punctuation">)</span> <span class="token operator">></span> maxAlloc<span class="token operator">/</span>sys<span class="token punctuation">.</span>PtrSize newcap <span class="token operator">=</span> <span class="token function">int</span><span class="token punctuation">(</span>capmem <span class="token operator">/</span> sys<span class="token punctuation">.</span>PtrSize<span class="token punctuation">)</span> <span class="token keyword">case</span> <span class="token function">isPowerOfTwo</span><span class="token punctuation">(</span>et<span class="token punctuation">.</span>size<span class="token punctuation">)</span><span class="token punctuation">:</span> <span class="token keyword">var</span> shift <span class="token builtin">uintptr</span> <span class="token keyword">if</span> sys<span class="token punctuation">.</span>PtrSize <span class="token operator">==</span> <span class="token number">8</span> <span class="token punctuation">{</span> <span class="token comment">// Mask shift for better code generation.</span> shift <span class="token operator">=</span> <span class="token function">uintptr</span><span class="token punctuation">(</span>sys<span class="token punctuation">.</span><span class="token function">Ctz64</span><span class="token punctuation">(</span><span class="token function">uint64</span><span class="token punctuation">(</span>et<span class="token punctuation">.</span>size<span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token operator">&</span> <span class="token number">63</span> <span class="token punctuation">}</span> <span class="token keyword">else</span> <span class="token punctuation">{</span> shift <span class="token operator">=</span> <span class="token function">uintptr</span><span class="token punctuation">(</span>sys<span class="token punctuation">.</span><span class="token function">Ctz32</span><span class="token punctuation">(</span><span class="token function">uint32</span><span class="token punctuation">(</span>et<span class="token punctuation">.</span>size<span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token operator">&</span> <span class="token number">31</span> <span class="token punctuation">}</span> lenmem <span class="token operator">=</span> <span class="token function">uintptr</span><span class="token punctuation">(</span>old<span class="token punctuation">.</span><span class="token builtin">len</span><span class="token punctuation">)</span> <span class="token operator"><<</span> shift newlenmem <span class="token operator">=</span> <span class="token function">uintptr</span><span class="token punctuation">(</span><span class="token builtin">cap</span><span class="token punctuation">)</span> <span class="token operator"><<</span> shift capmem <span class="token operator">=</span> <span class="token function">roundupsize</span><span class="token punctuation">(</span><span class="token function">uintptr</span><span class="token punctuation">(</span>newcap<span class="token punctuation">)</span> <span class="token operator"><<</span> shift<span class="token punctuation">)</span> overflow <span class="token operator">=</span> <span class="token function">uintptr</span><span class="token punctuation">(</span>newcap<span class="token punctuation">)</span> <span class="token operator">></span> <span class="token punctuation">(</span>maxAlloc <span class="token operator">>></span> shift<span class="token punctuation">)</span> newcap <span class="token operator">=</span> <span class="token function">int</span><span class="token punctuation">(</span>capmem <span class="token operator">>></span> shift<span class="token punctuation">)</span> <span class="token keyword">default</span><span class="token punctuation">:</span> lenmem <span class="token operator">=</span> <span class="token function">uintptr</span><span class="token punctuation">(</span>old<span class="token punctuation">.</span><span class="token builtin">len</span><span class="token punctuation">)</span> <span class="token operator">*</span> et<span class="token punctuation">.</span>size newlenmem <span class="token operator">=</span> <span class="token function">uintptr</span><span class="token punctuation">(</span><span class="token builtin">cap</span><span class="token punctuation">)</span> <span class="token operator">*</span> et<span class="token punctuation">.</span>size capmem<span class="token punctuation">,</span> overflow <span class="token operator">=</span> math<span class="token punctuation">.</span><span class="token function">MulUintptr</span><span class="token punctuation">(</span>et<span class="token punctuation">.</span>size<span class="token punctuation">,</span> <span class="token function">uintptr</span><span class="token punctuation">(</span>newcap<span class="token punctuation">)</span><span class="token punctuation">)</span> capmem <span class="token operator">=</span> <span class="token function">roundupsize</span><span class="token punctuation">(</span>capmem<span class="token punctuation">)</span> newcap <span class="token operator">=</span> <span class="token function">int</span><span class="token punctuation">(</span>capmem <span class="token operator">/</span> et<span class="token punctuation">.</span>size<span class="token punctuation">)</span> <span class="token punctuation">}</span> </code></pre>

它会根据你元素的类型从新给你计算一下你的新容量,这个时候得到的新容量才是你切片的真正容量,所以即使你没超过1024,也不一定就是翻倍了,而是根据你的元素类型的计算的.(<code>int32</code>是4个字节的元素,在这里4个字节的元素计算起来较为麻烦,这里就不多叙述了,下面我用一个int类型的元素详细说说)

我们接下来再看个例子:(我的电脑是64位)

<pre><code class="lang-go hljs"> slice <span class="token operator">:=</span> <span class="token function">make</span><span class="token punctuation">(</span><span class="token punctuation">[</span><span class="token punctuation">]</span><span class="token builtin">int64</span><span class="token punctuation">,</span> <span class="token number">3</span><span class="token punctuation">,</span><span class="token number">3</span><span class="token punctuation">)</span> fmt<span class="token punctuation">.</span><span class="token function">Println</span><span class="token punctuation">(</span><span class="token string">"len:"</span><span class="token punctuation">,</span> <span class="token function">len</span><span class="token punctuation">(</span>slice<span class="token punctuation">)</span><span class="token punctuation">,</span> <span class="token string">"cap:"</span><span class="token punctuation">,</span> <span class="token function">cap</span><span class="token punctuation">(</span>slice<span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token comment">// 3 3</span> slice <span class="token operator">=</span> <span class="token function">append</span><span class="token punctuation">(</span>slice<span class="token punctuation">,</span> <span class="token number">1</span><span class="token punctuation">)</span> fmt<span class="token punctuation">.</span><span class="token function">Println</span><span class="token punctuation">(</span><span class="token string">"len:"</span><span class="token punctuation">,</span> <span class="token function">len</span><span class="token punctuation">(</span>slice<span class="token punctuation">)</span><span class="token punctuation">,</span> <span class="token string">"cap:"</span><span class="token punctuation">,</span> <span class="token function">cap</span><span class="token punctuation">(</span>slice<span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token comment">// 4 6</span> <span class="token keyword">var</span> a <span class="token builtin">int64</span> fmt<span class="token punctuation">.</span><span class="token function">Println</span><span class="token punctuation">(</span>unsafe<span class="token punctuation">.</span><span class="token function">Sizeof</span><span class="token punctuation">(</span>a<span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token comment">// 8</span> </code></pre>

这个时候就是翻倍了,我们按上面的说法计算一下试试:

<ol><li>

<code>newcap := old.cap</code> =><code>newcap = 3</code>

</li><li>

<code>doublecap := newcap newcap</code> => <code>doublecap = 6</code>

</li><li>

<code>cap</code>为新切片的容量即:旧切片容量3 新<code>append</code>的1个元素=4

</li><li>

<code>cap < doublecap && old.len < 1024</code>所以<code>newcap = doublecap</code> => <code>newcap = 6</code>

</li><li>

由上我们可知<code>int64</code>为8位,我们现在再来这个<code>switch</code>语句中看看

</li><li>

此时我们进入的是<code>case et.size == sys.PtrSize</code>这个语句里(这个<code>sys.PtrSize</code>与你的系统位数有关,我这边得到的值是8)

</li><li>

<code>lenmem</code>与<code>newlenmem</code>我们可以先不关心,这个是用于把原切片中的元素拷贝到新的切片中去而使用的

</li><li>

<code>capmem = roundupsize(uintptr(newcap) * sys.PtrSize)</code>,这个<code>roundupsize</code>其实是一个内存对齐的过程

go中分配内存的规则如下(由源码我们可知<code>roundupsize</code>的传参是6*8=48,刚好有对应的48与之对应):

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

<code>capmem</code>最后返回的值是48,因此<code>newcap = 48 / 8= 6</code>

</li></ol>

有的盆友可能就会说了,你这个用例没有说服力啊,他还是翻倍了呀!,好,我给你举一个不是翻倍的例子:

<pre><code class="lang-go hljs">slice <span class="token operator">:=</span> <span class="token punctuation">[</span><span class="token punctuation">]</span><span class="token builtin">int64</span><span class="token punctuation">{</span><span class="token number">1</span><span class="token punctuation">,</span><span class="token number">2</span><span class="token punctuation">}</span> fmt<span class="token punctuation">.</span><span class="token function">Println</span><span class="token punctuation">(</span><span class="token string">"len:"</span><span class="token punctuation">,</span> <span class="token function">len</span><span class="token punctuation">(</span>slice<span class="token punctuation">)</span><span class="token punctuation">,</span> <span class="token string">"cap:"</span><span class="token punctuation">,</span> <span class="token function">cap</span><span class="token punctuation">(</span>slice<span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token comment">// 2 2</span> slice <span class="token operator">=</span> <span class="token function">append</span><span class="token punctuation">(</span>slice<span class="token punctuation">,</span> <span class="token number">3</span><span class="token punctuation">)</span> fmt<span class="token punctuation">.</span><span class="token function">Println</span><span class="token punctuation">(</span><span class="token string">"len:"</span><span class="token punctuation">,</span> <span class="token function">len</span><span class="token punctuation">(</span>slice<span class="token punctuation">)</span><span class="token punctuation">,</span> <span class="token string">"cap:"</span><span class="token punctuation">,</span> <span class="token function">cap</span><span class="token punctuation">(</span>slice<span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token comment">// 3 4</span> slice <span class="token operator">=</span> <span class="token function">append</span><span class="token punctuation">(</span>slice<span class="token punctuation">,</span> <span class="token number">4</span><span class="token punctuation">)</span> fmt<span class="token punctuation">.</span><span class="token function">Println</span><span class="token punctuation">(</span><span class="token string">"len:"</span><span class="token punctuation">,</span> <span class="token function">len</span><span class="token punctuation">(</span>slice<span class="token punctuation">)</span><span class="token punctuation">,</span> <span class="token string">"cap:"</span><span class="token punctuation">,</span> <span class="token function">cap</span><span class="token punctuation">(</span>slice<span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token comment">// 4 4</span> slice <span class="token operator">=</span> <span class="token function">append</span><span class="token punctuation">(</span>slice<span class="token punctuation">,</span> <span class="token number">5</span><span class="token punctuation">)</span> fmt<span class="token punctuation">.</span><span class="token function">Println</span><span class="token punctuation">(</span><span class="token string">"len:"</span><span class="token punctuation">,</span> <span class="token function">len</span><span class="token punctuation">(</span>slice<span class="token punctuation">)</span><span class="token punctuation">,</span> <span class="token string">"cap:"</span><span class="token punctuation">,</span> <span class="token function">cap</span><span class="token punctuation">(</span>slice<span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token comment">// 5 8</span> <span class="token keyword">var</span> a <span class="token builtin">int64</span> fmt<span class="token punctuation">.</span><span class="token function">Println</span><span class="token punctuation">(</span>unsafe<span class="token punctuation">.</span><span class="token function">Sizeof</span><span class="token punctuation">(</span>a<span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token comment">// 8</span> </code></pre>

这个咋一看没问题,都翻倍了是吧,我们接着往下看:

<pre><code class="lang-go hljs"> slice <span class="token operator">:=</span> <span class="token punctuation">[</span><span class="token punctuation">]</span><span class="token builtin">int64</span><span class="token punctuation">{</span><span class="token number">1</span><span class="token punctuation">,</span><span class="token number">2</span><span class="token punctuation">}</span> fmt<span class="token punctuation">.</span><span class="token function">Println</span><span class="token punctuation">(</span><span class="token string">"len:"</span><span class="token punctuation">,</span> <span class="token function">len</span><span class="token punctuation">(</span>slice<span class="token punctuation">)</span><span class="token punctuation">,</span> <span class="token string">"cap:"</span><span class="token punctuation">,</span> <span class="token function">cap</span><span class="token punctuation">(</span>slice<span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token comment">// 2 2</span> slice <span class="token operator">=</span> <span class="token function">append</span><span class="token punctuation">(</span>slice<span class="token punctuation">,</span> <span class="token number">3</span><span class="token punctuation">,</span><span class="token number">4</span><span class="token punctuation">,</span><span class="token number">5</span><span class="token punctuation">)</span> fmt<span class="token punctuation">.</span><span class="token function">Println</span><span class="token punctuation">(</span><span class="token string">"len:"</span><span class="token punctuation">,</span> <span class="token function">len</span><span class="token punctuation">(</span>slice<span class="token punctuation">)</span><span class="token punctuation">,</span> <span class="token string">"cap:"</span><span class="token punctuation">,</span> <span class="token function">cap</span><span class="token punctuation">(</span>slice<span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token comment">// 5 6</span> </code></pre>

这又是什么鬼???

我们接着往下看源码:

<pre><code class="lang-go hljs">newcap <span class="token operator">:=</span> old<span class="token punctuation">.</span><span class="token builtin">cap</span> <span class="token comment">// 2</span> doublecap <span class="token operator">:=</span> newcap <span class="token operator"> </span> newcap <span class="token comment">// 4</span> <span class="token comment">// cap = 2 3 = 5</span> <span class="token keyword">if</span> <span class="token builtin">cap</span> <span class="token operator">></span> doublecap <span class="token punctuation">{</span> newcap <span class="token operator">=</span> <span class="token builtin">cap</span> <span class="token punctuation">}</span> 此时newcap <span class="token operator">=</span> <span class="token number">5</span> 然后计算capmem<span class="token punctuation">:</span> capmem <span class="token operator">=</span> <span class="token function">roundupsize</span><span class="token punctuation">(</span><span class="token function">uintptr</span><span class="token punctuation">(</span>newcap<span class="token punctuation">)</span> <span class="token operator">*</span> sys<span class="token punctuation">.</span>PtrSize<span class="token punctuation">)</span> <span class="token comment">// uintptr(newcap) * sys.PtrSize = 5 * 8 =40</span> 根据上面内存分配的规则<span class="token punctuation">,</span>可知<span class="token number">32</span> <span class="token operator"><</span> <span class="token number">40</span> <span class="token operator"><</span> <span class="token number">48</span> 此时向上取整<span class="token punctuation">,</span>实际分配内存为<span class="token number">48</span> 所以newcap <span class="token operator">=</span> <span class="token function">int</span><span class="token punctuation">(</span>capmem <span class="token operator">/</span> sys<span class="token punctuation">.</span>PtrSize<span class="token punctuation">)</span> <span class="token operator">=</span> <span class="token number">48</span><span class="token operator">/</span><span class="token number">8</span> <span class="token operator">=</span> <span class="token number">6</span> </code></pre>

当然go的内存分配没这么简单,这里只讲述与<code>slice</code>相关的部分,至此你应该知道<code>slice</code>的扩容的基本原理了吧

<h2>
slice的截取</h2>

我们还要注意的是,扩容时如果容量大于原有数据的长度,我们重新分配内存,其操作不会影响原有的数据。但是没有分配新的内存,也就是说还是原来数组的基础上添加元素,那么新的切片操作就会影响原有的数组。这部分依然不再赘述,看一下下面代码,大家都明白了:

<pre><code> s := []int{1,2,3,4,5,6} fmt.Println(len(s), cap(s)) // 6 6 s2 := s[1:3] fmt.Println(len(s2), cap(s2))// 2 5 这个时候s2的底层数组是截取的s的一部分,所

您可能感兴趣的文章:
Go解密:数组、切片
想系统学习GO语言(Golang
深度解密Go语言之Slice
应用编程基础课第三讲:Go编程基础
饶全成:汇编角度看 Slice,一个新的世界
Go语言之父详述切片与数组的不同
理解 Go 编程中的 slice
[go语言]-slice实现的使用和基本原理
go 语言学习历程
Go语言的几大优势和特性

[关闭]
~ ~