slice初始化</h1>
简单了解了slice的底层结构后,我们来看下slice的初始化,在golang中slice有多重初始化方式,在这里我们就不一一介绍了,我们主要关注slice在底层是如何初始化的,首先我们来看一段代码:
<pre><code class="lang-go hljs"><span class="token keyword">package</span> main
<span class="token keyword">import</span> <span class="token string">"fmt"</span>
<span class="token keyword">func</span> <span class="token function">main</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
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">int</span><span class="token punctuation">,</span> <span class="token number">0</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>slice<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 function">cap</span><span class="token punctuation">(</span>slice<span class="token punctuation">)</span><span class="token punctuation">)</span>
<span class="token punctuation">}</span>
</code></pre>
很简单的一段代码,make一个slice,往slice中append一个一个1,打印slice内容,长度和容量,接下来我们利用gotool提供的工具将以上代码反汇编:
<pre><code class="lang-go hljs"><span class="token keyword">go</span> tool compile <span class="token operator">-</span>S slice<span class="token punctuation">.</span><span class="token keyword">go</span>
</code></pre>
得到汇编代码如下(截取部分):
<pre><code class="lang-go hljs"><span class="token number">0x0000</span> <span class="token number">00000</span> <span class="token punctuation">(</span>slice<span class="token punctuation">.</span><span class="token keyword">go</span><span class="token punctuation">:</span><span class="token number">8</span><span class="token punctuation">)</span> TEXT <span class="token string">""</span><span class="token punctuation">.</span><span class="token function">main</span><span class="token punctuation">(</span>SB<span class="token punctuation">)</span><span class="token punctuation">,</span> ABIInternal<span class="token punctuation">,</span> $<span class="token number">152</span><span class="token operator">-</span><span class="token number">0</span>
<span class="token number">0x0000</span> <span class="token number">00000</span> <span class="token punctuation">(</span>slice<span class="token punctuation">.</span><span class="token keyword">go</span><span class="token punctuation">:</span><span class="token number">8</span><span class="token punctuation">)</span> MOVQ <span class="token punctuation">(</span>TLS<span class="token punctuation">)</span><span class="token punctuation">,</span> CX
<span class="token number">0x0009</span> <span class="token number">00009</span> <span class="token punctuation">(</span>slice<span class="token punctuation">.</span><span class="token keyword">go</span><span class="token punctuation">:</span><span class="token number">8</span><span class="token punctuation">)</span> LEAQ <span class="token operator">-</span><span class="token function">24</span><span class="token punctuation">(</span>SP<span class="token punctuation">)</span><span class="token punctuation">,</span> AX
<span class="token number">0x000e</span> <span class="token number">00014</span> <span class="token punctuation">(</span>slice<span class="token punctuation">.</span><span class="token keyword">go</span><span class="token punctuation">:</span><span class="token number">8</span><span class="token punctuation">)</span> CMPQ AX<span class="token punctuation">,</span> <span class="token function">16</span><span class="token punctuation">(</span>CX<span class="token punctuation">)</span>
<span class="token number">0x0012</span> <span class="token number">00018</span> <span class="token punctuation">(</span>slice<span class="token punctuation">.</span><span class="token keyword">go</span><span class="token punctuation">:</span><span class="token number">8</span><span class="token punctuation">)</span> JLS <span class="token number">375</span>
<span class="token number">0x0018</span> <span class="token number">00024</span> <span class="token punctuation">(</span>slice<span class="token punctuation">.</span><span class="token keyword">go</span><span class="token punctuation">:</span><span class="token number">8</span><span class="token punctuation">)</span> SUBQ $<span class="token number">152</span><span class="token punctuation">,</span> SP
<span class="token number">0x001f</span> <span class="token number">00031</span> <span class="token punctuation">(</span>slice<span class="token punctuation">.</span><span class="token keyword">go</span><span class="token punctuation">:</span><span class="token number">8</span><span class="token punctuation">)</span> MOVQ BP<span class="token punctuation">,</span> <span class="token function">144</span><span class="token punctuation">(</span>SP<span class="token punctuation">)</span>
<span class="token number">0x0027</span> <span class="token number">00039</span> <span class="token punctuation">(</span>slice<span class="token punctuation">.</span><span class="token keyword">go</span><span class="token punctuation">:</span><span class="token number">8</span><span class="token punctuation">)</span> LEAQ <span class="token function">144</span><span class="token punctuation">(</span>SP<span class="token punctuation">)</span><span class="token punctuation">,</span> BP
<span class="token number">0x002f</span> <span class="token number">00047</span> <span class="token punctuation">(</span>slice<span class="token punctuation">.</span><span class="token keyword">go</span><span class="token punctuation">:</span><span class="token number">8</span><span class="token punctuation">)</span> FUNCDATA $<span class="token number">0</span><span class="token punctuation">,</span> gclocals<span class="token operator">-</span> <span class="token function">f14a5bc6d08bc46424827f54d2e3f8ed</span><span class="token punctuation">(</span>SB<span class="token punctuation">)</span><span class="token comment">//编译器产生,用于保存一些垃圾收集相关的信息</span>
<span class="token number">0x002f</span> <span class="token number">00047</span> <span class="token punctuation">(</span>slice<span class="token punctuation">.</span><span class="token keyword">go</span><span class="token punctuation">:</span><span class="token number">8</span><span class="token punctuation">)</span> FUNCDATA $<span class="token number">1</span><span class="token punctuation">,</span> gclocals<span class="token operator">-</span> <span class="token function">3e7bd269c75edba02eda3b9069a96409</span><span class="token punctuation">(</span>SB<span class="token punctuation">)</span>
<span class="token number">0x002f</span> <span class="token number">00047</span> <span class="token punctuation">(</span>slice<span class="token punctuation">.</span><span class="token keyword">go</span><span class="token punctuation">:</span><span class="token number">8</span><span class="token punctuation">)</span> FUNCDATA $<span class="token number">2</span><span class="token punctuation">,</span> gclocals<span class="token operator">-</span> <span class="token function">f6aec3988379d2bd21c69c093370a150</span><span class="token punctuation">(</span>SB<span class="token punctuation">)</span>
<span class="token number">0x002f</span> <span class="token number">00047</span> <span class="token punctuation">(</span>slice<span class="token punctuation">.</span><span class="token keyword">go</span><span class="token punctuation">:</span><span class="token number">8</span><span class="token punctuation">)</span> FUNCDATA $<span class="token number">3</span><span class="token punctuation">,</span> <span class="token string">""</span><span class="token punctuation">.</span>main<span class="token punctuation">.</span><span class="token function">stkobj</span><span class="token punctuation">(</span>SB<span class="token punctuation">)</span>
<span class="token number">0x002f</span> <span class="token number">00047</span> <span class="token punctuation">(</span>slice<span class="token punctuation">.</span><span class="token keyword">go</span><span class="token punctuation">:</span><span class="token number">9</span><span class="token punctuation">)</span> PCDATA $<span class="token number">0</span><span class="token punctuation">,</span> $<span class="token number">1</span>
<span class="token number">0x002f</span> <span class="token number">00047</span> <span class="token punctuation">(</span>slice<span class="token punctuation">.</span><span class="token keyword">go</span><span class="token punctuation">:</span><span class="token number">9</span><span class="token punctuation">)</span> PCDATA $<span class="token number">1</span><span class="token punctuation">,</span> $<span class="token number">0</span>
<span class="token number">0x002f</span> <span class="token number">00047</span> <span class="token punctuation">(</span>slice<span class="token punctuation">.</span><span class="token keyword">go</span><span class="token punctuation">:</span><span class="token number">9</span><span class="token punctuation">)</span> LEAQ <span class="token keyword">type</span><span class="token punctuation">.</span><span class="token function">int</span><span class="token punctuation">(</span>SB<span class="token punctuation">)</span><span class="token punctuation">,</span> AX
<span class="token number">0x0036</span> <span class="token number">00054</span> <span class="token punctuation">(</span>slice<span class="token punctuation">.</span><span class="token keyword">go</span><span class="token punctuation">:</span><span class="token number">9</span><span class="token punctuation">)</span> PCDATA $<span class="token number">0</span><span class="token punctuation">,</span> $<span class="token number">0</span>
<span class="token number">0x0036</span> <span class="token number">00054</span> <span class="token punctuation">(</span>slice<span class="token punctuation">.</span><span class="token keyword">go</span><span class="token punctuation">:</span><span class="token number">9</span><span class="token punctuation">)</span> MOVQ AX<span class="token punctuation">,</span> <span class="token punctuation">(</span>SP<span class="token punctuation">)</span>
<span class="token number">0x003a</span> <span class="token number">00058</span> <span class="token punctuation">(</span>slice<span class="token punctuation">.</span><span class="token keyword">go</span><span class="token punctuation">:</span><span class="token number">9</span><span class="token punctuation">)</span> XORPS X0<span class="token punctuation">,</span> X0
<span class="token number">0x003d</span> <span class="token number">00061</span> <span class="token punctuation">(</span>slice<span class="token punctuation">.</span><span class="token keyword">go</span><span class="token punctuation">:</span><span class="token number">9</span><span class="token punctuation">)</span> MOVUPS X0<span class="token punctuation">,</span> <span class="token function">8</span><span class="token punctuation">(</span>SP<span class="token punctuation">)</span>
<span class="token number">0x0042</span> <span class="token number">00066</span> <span class="token punctuation">(</span>slice<span class="token punctuation">.</span><span class="token keyword">go</span><span class="token punctuation">:</span><span class="token number">9</span><span class="token punctuation">)</span> CALL runtime<span class="token punctuation">.</span><span class="token function">makeslice</span><span class="token punctuation">(</span>SB<span class="token punctuation">)</span><span class="token comment">//初始化slice</span>
<span class="token number">0x0047</span> <span class="token number">00071</span> <span class="token punctuation">(</span>slice<span class="token punctuation">.</span><span class="token keyword">go</span><span class="token punctuation">:</span><span class="token number">9</span><span class="token punctuation">)</span> PCDATA $<span class="token number">0</span><span class="token punctuation">,</span> $<span class="token number">1</span>
<span class="token number">0x0047</span> <span class="token number">00071</span> <span class="token punctuation">(</span>slice<span class="token punctuation">.</span><span class="token keyword">go</span><span class="token punctuation">:</span><span class="token number">9</span><span class="token punctuation">)</span> MOVQ <span class="token function">24</span><span class="token punctuation">(</span>SP<span class="token punctuation">)</span><span class="token punctuation">,</span> AX
<span class="token number">0x004c</span> <span class="token number">00076</span> <span class="token punctuation">(</span>slice<span class="token punctuation">.</span><span class="token keyword">go</span><span class="token punctuation">:</span><span class="token number">10</span><span class="token punctuation">)</span> PCDATA $<span class="token number">0</span><span class="token punctuation">,</span> $<span class="token number">2</span>
<span class="token number">0x004c</span> <span class="token number">00076</span> <span class="token punctuation">(</span>slice<span class="token punctuation">.</span><span class="token keyword">go</span><span class="token punctuation">:</span><span class="token number">10</span><span class="token punctuation">)</span> LEAQ <span class="token keyword">type</span><span class="token punctuation">.</span><span class="token function">int</span><span class="token punctuation">(</span>SB<span class="token punctuation">)</span><span class="token punctuation">,</span> CX
<span class="token number">0x0053</span> <span class="token number">00083</span> <span class="token punctuation">(</span>slice<span class="token punctuation">.</span><span class="token keyword">go</span><span class="token punctuation">:</span><span class="token number">10</span><span class="token punctuation">)</span> PCDATA $<span class="token number">0</span><span class="token punctuation">,</span> $<span class="token number">1</span>
<span class="token number">0x0053</span> <span class="token number">00083</span> <span class="token punctuation">(</span>slice<span class="token punctuation">.</span><span class="token keyword">go</span><span class="token punctuation">:</span><span class="token number">10</span><span class="token punctuation">)</span> MOVQ CX<span class="token punctuation">,</span> <span class="token punctuation">(</span>SP<span class="token punctuation">)</span>
<span class="token number">0x0057</span> <span class="token number">00087</span> <span class="token punctuation">(</span>slice<span class="token punctuation">.</span><span class="token keyword">go</span><span class="token punctuation">:</span><span class="token number">10</span><span class="token punctuation">)</span> PCDATA $<span class="token number">0</span><span class="token punctuation">,</span> $<span class="token number">0</span>
<span class="token number">0x0057</span> <span class="token number">00087</span> <span class="token punctuation">(</span>slice<span class="token punctuation">.</span><span class="token keyword">go</span><span class="token punctuation">:</span><span class="token number">10</span><span class="token punctuation">)</span> MOVQ AX<span class="token punctuation">,</span> <span class="token function">8</span><span class="token punctuation">(</span>SP<span class="token punctuation">)</span>
<span class="token number">0x005c</span> <span class="token number">00092</span> <span class="token punctuation">(</span>slice<span class="token punctuation">.</span><span class="token keyword">go</span><span class="token punctuation">:</span><span class="token number">10</span><span class="token punctuation">)</span> XORPS X0<span class="token punctuation">,</span> X0
<span class="token number">0x005f</span> <span class="token number">00095</span> <span class="token punctuation">(</span>slice<span class="token punctuation">.</span><span class="token keyword">go</span><span class="token punctuation">:</span><span class="token number">10</span><span class="token punctuation">)</span> MOVUPS X0<span class="token punctuation">,</span> <span class="token function">16</span><span class="token punctuation">(</span>SP<span class="token punctuation">)</span>
<span class="token number">0x0064</span> <span class="token number">00100</span> <span class="token punctuation">(</span>slice<span class="token punctuation">.</span><span class="token keyword">go</span><span class="token punctuation">:</span><span class="token number">10</span><span class="token punctuation">)</span> MOVQ $<span class="token number">1</span><span class="token punctuation">,</span> <span class="token function">32</span><span class="token punctuation">(</span>SP<span class="token punctuation">)</span>
<span class="token number">0x006d</span> <span class="token number">00109</span> <span class="token punctuation">(</span>slice<span class="token punctuation">.</span><span class="token keyword">go</span><span class="token punctuation">:</span><span class="token number">10</span><span class="token punctuation">)</span> CALL runtime<span class="token punctuation">.</span><span class="token function">growslice</span><span class="token punctuation">(</span>SB<span class="token punctuation">)</span><span class="token comment">//append操作</span>
<span class="token number">0x0072</span> <span class="token number">00114</span> <span class="token punctuation">(</span>slice<span class="token punctuation">.</span><span class="token keyword">go</span><span class="token punctuation">:</span><span class="token number">10</span><span class="token punctuation">)</span> PCDATA $<span class="token number">0</span><span class="token punctuation">,</span> $<span class="token number">1</span>
<span class="token number">0x0072</span> <span class="token number">00114</span> <span class="token punctuation">(</span>slice<span class="token punctuation">.</span><span class="token keyword">go</span><span class="token punctuation">:</span><span class="token number">10</span><span class="token punctuation">)</span> MOVQ <span class="token function">40</span><span class="token punctuation">(</span>SP<span class="token punctuation">)</span><span class="token punctuation">,</span> AX
<span class="token number">0x0077</span> <span class="token number">00119</span> <span class="token punctuation">(</span>slice<span class="token punctuation">.</span><span class="token keyword">go</span><span class="token punctuation">:</span><span class="token number">10</span><span class="token punctuation">)</span> MOVQ <span class="token function">48</span><span class="token punctuation">(</span>SP<span class="token punctuation">)</span><span class="token punctuation">,</span> CX
<span class="token number">0x007c</span> <span class="token number">00124</span> <span class="token punctuation">(</span>slice<span class="token punctuation">.</span><span class="token keyword">go</span><span class="token punctuation">:</span><span class="token number">10</span><span class="token punctuation">)</span> MOVQ <span class="token function">56</span><span class="token punctuation">(</span>SP<span class="token punctuation">)</span><span class="token punctuation">,</span> DX
<span class="token number">0x0081</span> <span class="token number">00129</span> <span class="token punctuation">(</span>slice<span class="token punctuation">.</span><span class="token keyword">go</span><span class="token punctuation">:</span><span class="token number">10</span><span class="token punctuation">)</span> MOVQ DX<span class="token punctuation">,</span> <span class="token string">""</span><span class="token punctuation">.</span>slice<span class="token punctuation">.</span><span class="token builtin">cap</span><span class="token operator"> </span><span class="token function">72</span><span class="token punctuation">(</span>SP<span class="token punctuation">)</span>
<span class="token number">0x0086</span> <span class="token number">00134</span> <span class="token punctuation">(</span>slice<span class="token punctuation">.</span><span class="token keyword">go</span><span class="token punctuation">:</span><span class="token number">10</span><span class="token punctuation">)</span> MOVQ $<span class="token number">1</span><span class="token punctuation">,</span> <span class="token punctuation">(</span>AX<span class="token punctuation">)</span>
<span class="token number">0x008d</span> <span class="token number">00141</span> <span class="token punctuation">(</span>slice<span class="token punctuation">.</span><span class="token keyword">go</span><span class="token punctuation">:</span><span class="token number">11</span><span class="token punctuation">)</span> PCDATA $<span class="token number">0</span><span class="token punctuation">,</span> $<span class="token number">0</span>
<span class="token number">0x008d</span> <span class="token number">00141</span> <span class="token punctuation">(</span>slice<span class="token punctuation">.</span><span class="token keyword">go</span><span class="token punctuation">:</span><span class="token number">11</span><span class="token punctuation">)</span> MOVQ AX<span class="token punctuation">,</span> <span class="token punctuation">(</span>SP<span class="token punctuation">)</span>
<span class="token number">0x0091</span> <span class="token number">00145</span> <span class="token punctuation">(</span>slice<span class="token punctuation">.</span><span class="token keyword">go</span><span class="token punctuation">:</span><span class="token number">10</span><span class="token punctuation">)</span> LEAQ <span class="token function">1</span><span class="token punctuation">(</span>CX<span class="token punctuation">)</span><span class="token punctuation">,</span> AX
<span class="token number">0x0095</span> <span class="token number">00149</span> <span class="token punctuation">(</span>slice<span class="token punctuation">.</span><span class="token keyword">go</span><span class="token punctuation">:</span><span class="token number">10</span><span class="token punctuation">)</span> MOVQ AX<span class="token punctuation">,</span> <span class="token string">""</span><span class="token punctuation">.</span>slice<span class="token punctuation">.</span><span class="token builtin">len</span><span class="token operator"> </span><span class="token function">64</span><span class="token punctuation">(</span>SP<span class="token punctuation">)</span>
<span class="token number">0x009a</span> <span class="token number">00154</span> <span class="token punctuation">(</span>slice<span class="token punctuation">.</span><span class="token keyword">go</span><span class="token punctuation">:</span><span class="token number">11</span><span class="token punctuation">)</span> MOVQ AX<span class="token punctuation">,</span> <span class="token function">8</span><span class="token punctuation">(</span>SP<span class="token punctuation">)</span>
<span class="token number">0x009f</span> <span class="token number">00159</span> <span class="token punctuation">(</span>slice<span class="token punctuation">.</span><span class="token keyword">go</span><span class="token punctuation">:</span><span class="token number">11</span><span class="token punctuation">)</span> MOVQ DX<span class="token punctuation">,</span> <span class="token function">16</span><span class="token punctuation">(</span>SP<span class="token punctuation">)</span>
<span class="token number">0x00a4</span> <span class="token number">00164</span> <span class="token punctuation">(</span>slice<span class="token punctuation">.</span><span class="token keyword">go</span><span class="token punctuation">:</span><span class="token number">11</span><span class="token punctuation">)</span> CALL runtime<span class="token punctuation">.</span><span class="token function">convTslice</span><span class="token punctuation">(</span>SB<span class="token punctuation">)</span><span class="token comment">//类型转换</span>
<span class="token number">0x00a9</span> <span class="token number">00169</span> <span class="token punctuation">(</span>slice<span class="token punctuation">.</span><span class="token keyword">go</span><span class="token punctuation">:</span><span class="token number">11</span><span class="token punctuation">)</span> PCDATA $<span class="token number">0</span><span class="token punctuation">,</span> $<span class="token number">1</span>
<span class="token number">0x00a9</span> <span class="token number">00169</span> <span class="token punctuation">(</span>slice<span class="token punctuation">.</span><span class="token keyword">go</span><span class="token punctuation">:</span><span class="token number">11</span><span class="token punctuation">)</span> MOVQ <span class="token function">24</span><span class="token punctuation">(</span>SP<span class="token punctuation">)</span><span class="token punctuation">,</span> AX
<span class="token number">0x00ae</span> <span class="token number">00174</span> <span class="token punctuation">(</span>slice<span class="token punctuation">.</span><span class="token keyword">go</span><span class="token punctuation">:</span><span class="token number">11</span><span class="token punctuation">)</span> PCDATA $<span class="token number">0</span><span class="token punctuation">,</span> $<span class="token number">0</span>
<span class="token number">0x00ae</span> <span class="token number">00174</span> <span class="token punctuation">(</span>slice<span class="token punctuation">.</span><span class="token keyword">go</span><span class="token punctuation">:</span><span class="token number">11</span><span class="token punctuation">)</span> PCDATA $<span class="token number">1</span><span class="token punctuation">,</span> $<span class="token number">1</span>
<span class="token number">0x00ae</span> <span class="token number">00174</span> <span class="token punctuation">(</span>slice<span class="token punctuation">.</span><span class="token keyword">go</span><span class="token punctuation">:</span><span class="token number">11</span><span class="token punctuation">)</span> MOVQ AX<span class="token punctuation">,</span> <span class="token string">""</span><span class="token punctuation">.</span><span class="token punctuation">.</span>autotmp_33<span class="token operator"> </span><span class="token function">88</span><span class="token punctuation">(</span>SP<span class="token punctuation">)</span>
<span class="token number">0x00b3</span> <span class="token number">00179</span> <span class="token punctuation">(</span>slice<span class="token punctuation">.</span><span class="token keyword">go</span><span class="token punctuation">:</span><span class="token number">11</span><span class="token punctuation">)</span> MOVQ <span class="token string">""</span><span class="token punctuation">.</span>slice<span class="token punctuation">.</span><span class="token builtin">len</span><span class="token operator"> </span><span class="token function">64</span><span class="token punctuation">(</span>SP<span class="token punctuation">)</span><span class="token punctuation">,</span> CX
<span class="token number">0x00b8</span> <span class="token number">00184</span> <span class="token punctuation">(</span>slice<span class="token punctuation">.</span><span class="token keyword">go</span><span class="token punctuation">:</span><span class="token number">11</span><span class="token punctuation">)</span> MOVQ CX<span class="token punctuation">,</span> <span class="token punctuation">(</span>SP<span class="token punctuation">)</span>
<span class="token number">0x00bc</span> <span class="token number">00188</span> <span class="token punctuation">(</span>slice<span class="token punctuation">.</span><span class="token keyword">go</span><span class="token punctuation">:</span><span class="token number">11</span><span class="token punctuation">)</span> CALL runtime<span class="token punctuation">.</span><span class="token function">convT64</span><span class="token punctuation">(</span>SB<span class="token punctuation">)</span>
<span class="token number">0x00c1</span> <span class="token number">00193</span> <span class="token punctuation">(</span>slice<span class="token punctuation">.</span><span class="token keyword">go</span><span class="token punctuation">:</span><span class="token number">11</span><span class="token punctuation">)</span> PCDATA $<span class="token number">0</span><span class="token punctuation">,</span> $<span class="token number">1</span>
<span class="token number">0x00c1</span> <span class="token number">00193</span> <span class="token punctuation">(</span>slice<span class="token punctuation">.</span><span class="token keyword">go</span><span class="token punctuation">:</span><span class="token number">11</span><span class="token punctuation">)</span> MOVQ <span class="token function">8</span><span class="token punctuation">(</span>SP<span class="token punctuation">)</span><span class="token punctuation">,</span> AX
<span class="token number">0x00c6</span> <span class="token number">00198</span> <span class="token punctuation">(</span>slice<span class="token punctuation">.</span><span class="token keyword">go</span><span class="token punctuation">:</span><span class="token number">11</span><span class="token punctuation">)</span> PCDATA $<span class="token number">0</span><span class="token punctuation">,</span> $<span class="token number">0</span>
<span class="token number">0x00c6</span> <span class="token number">00198</span> <span class="token punctuation">(</span>slice<span class="token punctuation">.</span><span class="token keyword">go</span><span class="token punctuation">:</span><span class="token number">11</span><span class="token punctuation">)</span> PCDATA $<span class="token number">1</span><span class="token punctuation">,</span> $<span class="token number">2</span>
<span class="token number">0x00c6</span> <span class="token number">00198</span> <span class="token punctuation">(</span>slice<span class="token punctuation">.</span><span class="token keyword">go</span><span class="token punctuation">:</span><span class="token number">11</span><span class="token punctuation">)</span> MOVQ AX<span class="token punctuation">,</span> <span class="token string">""</span><span class="token punctuation">.</span><span class="token punctuation">.</span>autotmp_34<span class="token operator"> </span><span class="token function">80</span><span class="token punctuation">(</span>SP<span class="token punctuation">)</span>
<span class="token number">0x00cb</span> <span class="token number">00203</span> <span class="token punctuation">(</span>slice<span class="token punctuation">.</span><span class="token keyword">go</span><span class="token punctuation">:</span><span class="token number">11</span><span class="token punctuation">)</span> MOVQ <span class="token string">""</span><span class="token punctuation">.</span>slice<span class="token punctuation">.</span><span class="token builtin">cap</span><span class="token operator"> </span><span class="token function">72</span><span class="token punctuation">(</span>SP<span class="token punctuation">)</span><span class="token punctuation">,</span> CX
<span class="token number">0x00d0</span> <span class="token number">00208</span> <span class="token punctuation">(</span>slice<span class="token punctuation">.</span><span class="token keyword">go</span><span class="token punctuation">:</span><span class="token number">11</span><span class="token punctuation">)</span> MOVQ CX<span class="token punctuation">,</span> <span class="token punctuation">(</span>SP<span class="token punctuation">)</span>
<span class="token number">0x00d4</span> <span class="token number">00212</span> <span class="token punctuation">(</span>slice<span class="token punctuation">.</span><span class="token keyword">go</span><span class="token punctuation">:</span><span class="token number">11</span><span class="token punctuation">)</span> CALL runtime<span class="token punctuation">.</span><span class="token function">convT64</span><span class="token punctuation">(</span>SB<span class="token punctuation">)</span>
<span class="token number">0x00d9</span> <span class="token number">00217</span> <span class="token punctuation">(</span>slice<span class="token punctuation">.</span><span class="token keyword">go</span><span class="token punctuation">:</span><span class="token number">11</span><span class="token punctuation">)</span> PCDATA $<span class="token number">0</span><span class="token punctuation">,</span> $<span class="token number">1</span>
<span class="token number">0x00d9</span> <span class="token number">00217</span> <span class="token punctuation">(</span>slice<span class="token punctuation">.</span><span class="token keyword">go</span><span class="token punctuation">:</span><span class="token number">11</span><span class="token punctuation">)</span> MOVQ <span class="token function">8</span><span class="token punctuation">(</span>SP<span class="token punctuation">)</span><span class="token punctuation">,</span> AX
<span class="token number">0x00de</span> <span class="token number">00222</span> <span class="token punctuation">(</span>slice<span class="token punctuation">.</span><span class="token keyword">go</span><span class="token punctuation">:</span><span class="token number">11</span><span class="token punctuation">)</span> PCDATA $<span class="token number">1</span><span class="token punctuation">,</span> $<span class="token number">3</span>
<span class="token number">0x00de</span> <span class="token number">00222</span> <span class="token punctuation">(</span>slice<span class="token punctuation">.</span><span class="token keyword">go</span><span class="token punctuation">:</span><span class="token number">11</span><span class="token punctuation">)</span> XORPS X0<span class="token punctuation">,</span> X0
</code></pre>
大家可能看到这里有点蒙,这是在干啥,其实我们只需要关注一些关键的信息就好了,主要是这几行:
<pre><code class="lang-go hljs"><span class="token number">0x0042</span> <span class="token number">00066</span> <span class="token punctuation">(</span>slice<span class="token punctuation">.</span><span class="token keyword">go</span><span class="token punctuation">:</span><span class="token number">9</span><span class="token punctuation">)</span> CALL runtime<span class="token punctuation">.</span><span class="token function">makeslice</span><span class="token punctuation">(</span>SB<span class="token punctuation">)</span><span class="token comment">//初始化slice</span>
<span class="token number">0x006d</span> <span class="token number">00109</span> <span class="token punctuation">(</span>slice<span class="token punctuation">.</span><span class="token keyword">go</span><span class="token punctuation">:</span><span class="token number">10</span><span class="token punctuation">)</span> CALL runtime<span class="token punctuation">.</span><span class="token function">growslice</span><span class="token punctuation">(</span>SB<span class="token punctuation">)</span><span class="token comment">//append操作</span>
<span class="token number">0x00a4</span> <span class="token number">00164</span> <span class="token punctuation">(</span>slice<span class="token punctuation">.</span><span class="token keyword">go</span><span class="token punctuation">:</span><span class="token number">11</span><span class="token punctuation">)</span> CALL runtime<span class="token punctuation">.</span><span class="token function">convTslice</span><span class="token punctuation">(</span>SB<span class="token punctuation">)</span><span class="token comment">//类型转换</span>
<span class="token number">0x00bc</span> <span class="token number">00188</span> <span class="token punctuation">(</span>slice<span class="token punctuation">.</span><span class="token keyword">go</span><span class="token punctuation">:</span><span class="token number">11</span><span class="token punctuation">)</span> CALL runtime<span class="token punctuation">.</span><span class="token function">convT64</span><span class="token punctuation">(</span>SB<span class="token punctuation">)</span>
<span class="token number">0x00d4</span> <span class="token number">00212</span> <span class="token punctuation">(</span>slice<span class="token punctuation">.</span><span class="token keyword">go</span><span class="token punctuation">:</span><span class="token number">11</span><span class="token punctuation">)</span> CALL runtime<span class="token punctuation">.</span><span class="token function">convT64</span><span class="token punctuation">(</span>SB<span class="token punctuation">)</span>
</code></pre>
我们能观察出,底层是调用runtime中的makeslice方法来创建slice的,我们来看一下makeslice函数到底做了什么。
<pre><code class="lang-go hljs">
<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 comment">// Allocate an object of size bytes.</span>
<span class="token comment">// Small objects are allocated from the per-P cache's free lists.</span>
<span class="token comment">// Large objects (> 32 kB) are allocated straight from the heap.</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 keyword">func</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">panic</span><span class="token punctuation">(</span><span class="token function">errorString</span><span class="token punctuation">(</span><span class="token string">"makeslice: len out of range"</span><span class="token punctuation">)</span><span class="token punctuation">)</span>
<span class="token punctuation">}</span>
<span class="token keyword">func</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 function">panic</span><span class="token punctuation">(</span><span class="token function">errorString</span><span class="token punctuation">(</span><span class="token string">"makeslice: cap out of range"</span><span class="token punctuation">)</span><span class="token punctuation">)</span>
<span class="token punctuation">}</span>
</code></pre>
MulUintptr函数源码:
<pre><code class="lang-go hljs">
<span class="token keyword">package</span> math
<span class="token keyword">import</span> <span class="token string">"runtime/internal/sys"</span>
<span class="token keyword">const</span> MaxUintptr <span class="token operator">=</span> <span class="token operator">^</span><span class="token function">uintptr</span><span class="token punctuation">(</span><span class="token number">0</span><span class="token punctuation">)</span>
<span class="token comment">// MulUintptr returns a * b and whether the multiplication overflowed.</span>
<span class="token comment">// On supported platforms this is an intrinsic lowered by the compiler.</span>
<span class="token keyword">func</span> <span class="token function">MulUintptr</span><span class="token punctuation">(</span>a<span class="token punctuation">,</span> b <span class="token builtin">uintptr</span><span class="token punctuation">)</span> <span class="token punctuation">(</span><span class="token builtin">uintptr</span><span class="token punctuation">,</span> <span class="token builtin">bool</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
<span class="token keyword">if</span> a<span class="token operator">|</span>b <span class="token operator"><</span> <span class="token number">1</span><span class="token operator"><<</span><span class="token punctuation">(</span><span class="token number">4</span><span class="token operator">*</span>sys<span class="token punctuation">.</span>PtrSize<span class="token punctuation">)</span> <span class="token operator">||</span> a <span class="token operator">==</span> <span class="token number">0</span> <span class="token punctuation">{</span><span class="token comment">//a|b < 1<<(4*8)</span>
<span class="token keyword">return</span> a <span class="token operator">*</span> b<span class="token punctuation">,</span> <span class="token boolean">false</span>
<span class="token punctuation">}</span>
overflow <span class="token operator">:=</span> b <span class="token operator">></span> MaxUintptr<span class="token operator">/</span>a
<span class="token keyword">return</span> a <span class="token operator">*</span> b<span class="token punctuation">,</span> overflow
<span class="token punctuation">}</span>
</code></pre>
简单来说,makeslice函数的工作主要就是计算slice所需内存大小,然后调用mallocgc进行内存的分配。计算slice所需内存又是通过MulUintptr来实现的,MulUintptr的源码我们也已经贴出,主要就是用切片中元素大小和切片的容量相乘计算出所需占用的内存空间,如果内存溢出,或者计算出的内存大小大于最大可分配内存,MulUintptr的overflow会返回true,makeslice就会报错。另外如果传入长度小于0或者长度小于容量,makeslice也会报错。
<h1>append操作</h1>
首先我们来看一段程序:
<pre><code class="lang-go hljs"><span class="token keyword">package</span> main
<span class="token keyword">import</span> <span class="token punctuation">(</span>
<span class="token string">"fmt"</span>
<span class="token string">"unsafe"</span>
<span class="token punctuation">)</span>
<span class="token keyword">func</span> <span class="token function">main</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
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">int</span><span class="token punctuation">,</span> <span class="token number">0</span><span class="token punctuation">,</span> <span class="token number">10</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>unsafe<span class="token punctuation">.</span><span class="token function">Pointer</span><span class="token punctuation">(</span><span class="token operator">&</span>slice<span class="token punctuation">[</span><span class="token number">0</span><span class="token punctuation">]</span><span class="token punctuation">)</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 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">2</span><span class="token punctuation">)</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">Pointer</span><span class="token punctuation">(</span><span class="token operator">&</span>slice<span class="token punctuation">[</span><span class="token number">0</span><span class="token punctuation">]</span><span class="token punctuation">)</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 function">cap</span><span class="token punctuation">(</span>slice<span class="token punctuation">)</span><span class="token punctuation">)</span>
<span class="token punctuation">}</span>
</code></pre>
我们直接给出结果:
<pre><code class="lang-go hljs"><span class="token number">0xc00009e000</span> <span class="token number">1</span> <span class="token number">10</span>
<span class="token number">0xc00009e000</span> <span class="token number">2</span> <span class="token number">10</span>
</code></pre>
我们可以看到,当slice容量足够时,我们往slice中append一个2,slice底层数组指向的内存地址没有发生改变;再看一段程序:
<pre><code class="lang-go hljs"><span class="token keyword">func</span> <span class="token function">main</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
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">int</span><span class="token punctuation">,</span> <span class="token number">0</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">Printf</span><span class="token punctuation">(</span><span class="token string">"%p %d %d\n"</span><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>slice<span class="token punctuation">[</span><span class="token number">0</span><span class="token punctuation">]</span><span class="token punctuation">)</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 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">2</span><span class="token punctuation">)</span>
fmt<span class="token punctuation">.</span><span class="token function">Printf</span><span class="token punctuation">(</span><span class="token string">"%p %d %d\n"</span><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>slice<span class="token punctuation">[</span><span class="token number">0</span><span class="token punctuation">]</span><span class="token punctuation">)</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 function">cap</span><span class="token punctuation">(</span>slice<span class="token punctuation">)</span><span class="token punctuation">)</span>
<span class="token punctuation">}</span>
</code></pre>
输出结果是:
<pre><code class="lang-go hljs"><span class="token number">0xc00009a008</span> <span class="token number">1</span> <span class="token number">1</span>
<span class="token number">0xc00009a030</span> <span class="token number">2</span> <span class="token number">2</span>
</code></pre>
我们可以看到当往slice中append一个1后,slice底层数组的指针指向地址0xc00009a008,长度为1,容量为1。这时再往slice中append一个2,那么slice的容量不够了,此时底层数组会发生copy,会重新分配一块新的内存地址,容量也变成了2,所以我们会看到底层数组的指针指向地址发生了改变。根据之前汇编的结果我们知晓了,append操作其实是调用了runtime/slice.go中的growslice函数,我们来看下源码:
<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 operator">...</span>
<span class="token operator">...</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><span class="token comment">//1280</span>
doublecap <span class="token operator">:=</span> newcap <span class="token operator"> </span> newcap<span class="token comment">//1280 1280=2560</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> <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 comment">//1280*1.25=1600</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 operator">...</span>
<span class="token punctuation">}</span>
</code></pre>
我们主要关注下cap的扩容规则,从源码中我们可以简单的总结出slice容量的扩容规则:当原slice的cap小于1024时,新slice的cap变为原来的2倍;原slice的cap大于1024时,新slice变为原来的1.25倍,我们写个程序来验证下:
<pre><code class="lang-go hljs"><span class="token keyword">package</span> main
<span class="token keyword">import</span> <span class="token string">"fmt"</span>
<span class="token keyword">func</span> <span class="token function">main</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
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">int</span><span class="token punctuation">,</span> <span class="token number">0</span><span class="token punctuation">)</span>
oldCap <span class="token operator">:=</span> Golang 切片(slice)扩容机制源码剖析