返回
Featured image of post 深入解析Go语言中的零值与空值处理

深入解析Go语言中的零值与空值处理

这真的是最可恶的事情之一了

零值机制 Zero Value

Go语言所有类型变量在声明后都会自动初始化,这种默认初始化值称为零值。这是Go语言保证程序安全性的重要设计。

基础类型零值

1
2
3
4
5
6
var (
    a int     // 0
    b float64 // 0.0
    c string  // ""
    d bool    // false
)

符合类型零值

1
2
3
4
5
6
7
8
9
var (
    e *int         // nil(指针)
    f []int        // nil(切片)
    g map[int]int  // nil(字典)
    h chan int     // nil(通道)
    i func()       // nil(函数)
    j interface{}  // nil(接口)
    k struct{}     // 空结构体(所有字段零值)
)

nil 是什么

nil是一个只适用于特定类型的预定义的标识符

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
// 有效类型
var a *int = nil     // 指针
var b []int = nil    // 切片
var c map[int]int = nil // 字典
var d chan int = nil // 通道
var e func() = nil   // 函数
var f interface{} = nil // 接口

// 无效类型(编译错误)
var g int = nil      // 基础类型不允许
var h struct{} = nil // 结构体不允许

它在内存层面表现为以下几种情况

类型 内存表现 可用性
切片 所有字段为 0 可以添加元素但需要先初始化
映射 所有字段为 0 不能直接添加键值对,需要先make
通道 所有字段为 0 不能直接收发数据,需要先make
指针 地址为 0 不能直接解引用,会panic
接口 类型和值均为 0 可以调用方法但会panic
函数 函数地址为 0 不能直接调用,会panic

空值(nil)在切片的特殊处理

可以对nil切片进行append操作

我们可以对 nil切片执行append操作,对于nil函数、字典、接口的操作是会panic

1
2
3
4
5
6
7
var s []int        // nil切片
s = append(s, 1)   // 合法操作,自动初始化底层数组

// 安全操作建议:
if len(s) > 0 {    // 推荐使用长度判断
    fmt.Println(s[0])
}

nil 切片append时候的生命历程

初始的时候是len=0cap=0的一个切片

1
var s []int        // 底层结构:{array: nil, len: 0, cap: 0}

在进行append的时候会自动完成以下步骤

1
s = append(s, 1)   // 自动完成以下步骤:
  1. 检查当前容量是否足够 → 0 < 1 → 需要扩容
  2. 分配新数组(最小初始容量为1)
  3. 复制元素(此时无旧元素)
  4. 返回新切片:{array: 新地址, len:1, cap:1}

与普通切片相比

操作 nil 切片 空切片(make([]int,0)) 预分配切片(make([]int,0,5))
初始容量 0 0 5
首次append扩容策略 分配容量1 分配容量1 直接使用现有容量
内存分配时机 首次append时 首次append时 创建时已分配
底层数组地址 动态变化 动态变化 固定直到扩容

最优实践

  1. 随意对nil切片进行append
  2. 对于nil字典必须先进行make
  3. 利用nil切片表示无数据,利用空切片表示空集
Licensed under CC BY-NC-SA 4.0
© 2023 - 2025 壹壹贰捌· 0Days
共书写了258.6k字·共 93篇文章 京ICP备2023035941号-1