Golang中切片var a []int在首次使用前是否分配内存?

Golang中切片var a []int在首次使用前是否分配内存? 它不会分配相当于两个整型的内存吗:一个用于存储长度0,另一个用于存储指向底层数组的nil指针?

它会在首次使用时分配这两个整型吗?

另外,你更倾向于哪一种: var a []int VS a := []int{}

这个问题是倒数第二个,在底部,来自这里 https://www.toptal.com/go/interview-questions

5 回复

[]Type 的零值是 nil,因此在实际创建非零值之前无需进行分配。

此外,var a []int 并不等同于 a := []int{},因为后者应该已经用非零值进行了初始化。

更多关于Golang中切片var a []int在首次使用前是否分配内存?的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html


你好 @petrus@NobbZ

那么在运行时分配的是: 对于 var a []int

nil   <-  但这个 nil 值的类型是 "int 切片"

对于 t := []string{}

type slice struct {  <- 3 个字段,每个字段一个 int
	array nil
	len   0
	cap   0
}

这就是区别吗?

@petrus

说得对,总结一下: 所以在运行时分配的是: 对于 var a []int

type slice struct {  <- 3个字段,每个字段一个int
	array nil  <- nil指针
	len   0
	cap   0
}

对于 t := []string{}

type slice struct {  <- 3个字段,每个字段一个int
	array 0x-...  <- 一个实际的指针
	len   0
	cap   0
}

从内存角度看,它们占用的空间相同。一个具有 nil 内部指针,另一个具有非 nil 内部指针。

JOhn_Stuart: 这是区别吗?

不。这才是区别。

package main

import (
	"fmt"
	"unsafe"
)

type slice struct {
	array unsafe.Pointer
	len   int
	cap   int
}

func main() {
	var a []int
	printSlice(&a)
	fmt.Println()
	b := []int{}
	printSlice(&b)
	fmt.Println()
}

func printSlice(p *[]int) {
	s := *p
	fmt.Printf("%[1]v %[2]p %[1]T %[3]t\n", s, p, s == nil)
	ss := (*slice)(unsafe.Pointer(p))
	fmt.Println(ss.array, ss.len, ss.cap)
}
[] 0xc00000c0a0 []int true
<nil> 0 0

[] 0xc00000c0c0 []int false
0x586a00 0 0

ab 的声明在地址 0xc00000c0a0 和 0xc00000c0c0 处分配了零值切片结构体。将复合字面量 []int{} 赋值给 b 则在地址 0x586a00 处分配了一个底层数组。

在Go语言中,var a []int 声明一个切片,但不会分配底层数组的内存。它确实会分配一个切片头(slice header),其中包含三个字段:指向底层数组的指针(nil)、长度(0)和容量(0)。切片头本身会分配内存,通常占用24字节(64位系统上,每个字段8字节)。

切片头的内存分配发生在声明时,而不是首次使用时。例如:

var a []int // 切片头已分配,指针为nil,长度和容量为0
fmt.Println(a == nil) // 输出:true

对于 a := []int{},这会创建一个非nil的空切片。它同样会分配切片头,并且可能分配一个底层数组(具体取决于编译器优化)。但即使底层数组可能被优化掉,切片头仍然是非nil的。例如:

a := []int{} // 切片头已分配,指针可能指向一个零大小的数组或为nil(取决于编译器)
fmt.Println(a == nil) // 输出:false

性能上,var a []int 通常更优,因为它避免了可能的底层数组分配。使用 []int{} 时,编译器可能分配一个零大小的数组,这可能导致额外的内存分配。

实际选择取决于需求:

  • 如果需要表示“未初始化”的切片,使用 var a []int(nil切片)。
  • 如果需要空但已初始化的切片,使用 a := []int{}(空切片)。

示例代码:

package main

import "fmt"

func main() {
    var nilSlice []int
    emptySlice := []int{}
    
    fmt.Printf("nilSlice: %v, len=%d, cap=%d, is nil? %t\n", 
        nilSlice, len(nilSlice), cap(nilSlice), nilSlice == nil)
    fmt.Printf("emptySlice: %v, len=%d, cap=%d, is nil? %t\n", 
        emptySlice, len(emptySlice), cap(emptySlice), emptySlice == nil)
}

输出:

nilSlice: [], len=0, cap=0, is nil? true
emptySlice: [], len=0, cap=0, is nil? false

在大多数情况下,var a []int 是更安全且性能更好的选择,除非明确需要一个非nil的空切片。

回到顶部