切片文字和make slice在行为上有区别吗?

问题描述

注意: Declare slice or make slice?不能回答我的问题,因为那是将slice声明与make slice进行比较,而我的问题是将slice文字与make slice进行比较。该问题的答案很简单,因为裸切片声明会创建一个nil切片,但是,如果您仔细阅读下面的问题,我根本不会创建nil切片。

有两种方法可以创建切片并将其附加到切片。下面的代码将两种方式分别显示Example 1Example 2

package main

import (
    "fmt"
)

func main() {
    // Example 1
    a := []int{}
    fmt.Printf("len(a): %d; cap(a): %d; a: %v\n",len(a),cap(a),a)
    a = append(a,10,20,30,40,50)
    fmt.Printf("len(a): %d; cap(a): %d; a: %v\n",a)
    
    // Example 2
    b := make([]int,0)
    fmt.Printf("len(b): %d; cap(b): %d; b: %v\n",len(b),cap(b),b)
    b = append(b,50)
    fmt.Printf("len(b): %d; cap(b): %d; b: %v\n",b)
}

输出

len(a): 0; cap(a): 0; a: []
len(a): 5; cap(a): 6; a: [10 20 30 40 50]
len(b): 0; cap(b): 0; b: []
len(b): 5; cap(b): 6; b: [10 20 30 40 50]

是否有两种方法创建等效的[]int{}make([]int,0)空切片?他们在行为上有什么改变吗?

解决方法

我稍微修改了您的示例

    // Example 1
    a := []int{}
    pa := &a
  
    // Example 2
    b := make([]int,0)
    pb := &b

    runtime.KeepAlive(pa)
    runtime.KeepAlive(pb)

它编译为:

*** main.go#12   >    a := []int{}
0x4e56a9    488d0538bb1100          lea rax,ptr [runtime.zerobase]
0x4e56b0    4889442470          mov qword ptr [rsp+0x70],rax
0x4e56b5    8400                test byte ptr [rax],al
0x4e56b7    eb00                jmp 0x4e56b9
0x4e56b9    4889842418010000        mov qword ptr [rsp+0x118],rax
0x4e56c1    0f57c0              xorps xmm0,xmm0
0x4e56c4    0f11842420010000        movups xmmword ptr [rsp+0x120],xmm0
*** main.go#13   >    pa := &a
0x4e56cc    488d842418010000        lea rax,ptr [rsp+0x118]
0x4e56d4    4889442460          mov qword ptr [rsp+0x60],rax
*** main.go#16   >    b := make([]int,0)
0x4e56d9    488d0520020100          lea rax,ptr [__image_base__+1005824]
0x4e56e0    48890424            mov qword ptr [rsp],rax
0x4e56e4    0f57c0              xorps xmm0,xmm0
0x4e56e7    0f11442408          movups xmmword ptr [rsp+0x8],xmm0
0x4e56ec    e8bf49f6ff          call $runtime.makeslice
0x4e56f1    488b442418          mov rax,qword ptr [rsp+0x18]
0x4e56f6    4889842400010000        mov qword ptr [rsp+0x100],rax
0x4e56fe    0f57c0              xorps xmm0,xmm0
0x4e5701    0f11842408010000        movups xmmword ptr [rsp+0x108],xmm0
*** main.go#17   >    pb := &b
0x4e5709    488d842400010000        lea rax,ptr [rsp+0x100]
0x4e5711    4889442458          mov qword ptr [rsp+0x58],rax

似乎make([]int,0)会导致堆分配(通过$runtime.makeslice()),但不会-深入sources表示makeslice()还返回了基于&zerobase的切片:

    if size == 0 {
        return unsafe.Pointer(&zerobase)
    }

因此,这两个代码段都具有相同的切片结构,其中数据指针设置为zerobase