nil,空接口,空结构体有什么区别
Laiyong Wang Lv5

我的总结

nil是空,并不一定是空指针
nil是六种类型的零值
每种类型的nil是不同的,不能比较

空结构体

是go中特殊的类型
空结构体的值不是nill
指针也不是nill,但是都是相同(zerobase)

空接口
空接口的值也不一定是nil
类型为nil,值为nil才是nil接口

AI根据我的总结使用代码解释并完善

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
package main

import (
"fmt"
"unsafe"
)

func main() {
// 示例1: 不同类型nil的比较
var p *int
var s []int
fmt.Println(p == nil) // true
fmt.Println(s == nil) // true
// fmt.Println(p == s) // 编译错误:类型不匹配

// 示例2: 空结构体
var e1 struct{}
var e2 struct{}
fmt.Printf("size: %d\n", unsafe.Sizeof(e1)) // 0
fmt.Printf("addr: %p %p\n", &e1, &e2) // 相同地址(zerobase)
fmt.Println(&e1 == &e2) // true

// 示例3: 空接口判断
var a interface{} = (*int)(nil)
fmt.Println(a == nil) // false(类型为*int,值为nil)

var b interface{} = nil
fmt.Println(b == nil) // true(类型和值均为nil)

// 示例4: 接口变量赋值
var c *int
var d interface{} = c
fmt.Println(d == nil) // false(类型为*int,值为nil)
}

完整解释:

1. nil的本质
• 是6种类型的零值:指针、接口、切片、map、channel、函数
• 不同类型的nil不相等,内存表示不同

1
2
3
4
var p *int
var s []int
fmt.Printf("%p\n", p) // 0x0
fmt.Printf("%#v\n", s) // []int(nil)

2. 空结构体 struct{}
• 实例不占内存(size=0)
• 所有实例共享同一内存地址(zerobase)
• 常用于信号传递(channel信号)、实现Set

1
2
3
4
5
6
7
8
9
// 作为方法接收器
type MyType struct{}
func (t MyType) Print() {
fmt.Println("不需要状态")
}

// 实现Set
set := make(map[string]struct{})
set["key"] = struct{}{}

3. 空接口 interface{}
• 由类型指针和数据指针组成(runtime.eface)
• 双重nil才等于nil

1
2
3
4
5
6
7
8
9
// 接口内部结构示意
type eface struct {
_type *_type
data unsafe.Pointer
}

var a interface{} = (*int)(nil)
// _type指向int类型元数据
// data = 0x0

关键区别表格

特性 nil struct{} interface{}
内存占用 类型决定 0字节 16字节(2指针)
相等性 同类型相等 地址始终相同 类型+值双nil才相等
典型用途 表示缺失值 占位符/信号 泛型容器
指针比较 0x0 zerobase地址 依赖存储内容