您的位置 首页 golang

Go里的nil

在日常Golang使用中,你有没有这样的疑惑?

nil是什么?哪些可以用nil?哪些不能用nil?

接下来,我将对这些内容进行总结。

一、什么是nil

首先nil是一个变量,我们可以在源码包中找到这样的描述:

 // nil is a predeclared identifier representing the zero value for a
// pointer, channel, func, interface, map, or slice type.
var nil Type // Type must be a pointer, channel, func, interface, map, or slice type

// Type is here for the purposes of documentation only. It is a stand-in
// for any Go type, but represents the same type for any given function
// invocation.
type Type int  

从类型定义上可以得到以下关键点:

  • nil本质上是一个Type类型的变量
  • Type类型仅仅是基于int定义出来的新类型
  • nil适用于指针、channel、函数、interface、map、slice六种类型

二、六大类型

1、指针

1.1、变量定义

 var ptr *int  

1.2、变量本身

变量本身是8字节的内存块

1.3、nil赋值

这8个字节指针置0

1.4、nil判断

判断这8个字节是否为0

2、channel

2.1、变量定义

 // 变量本身定义
var c1 chan struct{}
// 变量定义和初始化
var c2 = make(chan struct{})  
  • 第一种方式仅仅定义了c1变量本身
  • 第二种方式则分配了c2的内存,调用了runtime下的makechan函数来创建结构

2.2、变量本身

一个 8 字节的指针而已,指向一个 channel 管理结构,也就是 struct hchan 的指针。

2.3、nil赋值

赋值 nil 之后,仅仅是把这 8 字节的指针置 0 。

2.4、nil判断

判断这指针是否为0

3、map

3.1、变量定义

 // 变量定义
var m1 map[int]int
// 变量定义和初始化
var m2 = make(map[int]int)  
  • 第一种方式仅仅定义了m1变量本身
  • 第二种方式则分配了m2的内存,调用了runtime下的makemap函数来创建结构

3.2、变量本身

变量本身是个指针

 // A header for a Go map.
type hmap struct {
// Note: the format of the hmap is also encoded in cmd/compile/internal/gc/reflect.go.
// Make sure this stays in sync with the compiler's definition.
// 键值对的数量
count int // # live cells == size of map.  Must be first (used by len() builtin)
// 标识状态
flags uint8
// 2^B = len(buckets)
B uint8 // log_2 of # of buckets (can hold up to loadFactor * 2^B items)
// 溢出桶里bmap大致数量
noverflow uint16 // approximate number of overflow buckets; see incrnoverflow for details
// hash因子
hash0 uint32 // hash seed
// 指向一个数组(连续内存空间),数组类型为[]bmap,bmap类型就是存在键值对的结构
buckets unsafe.Pointer // array of 2^B Buckets. may be nil if count==0.
// 扩容时,存放之前的buckets
oldbuckets unsafe.Pointer // previous bucket array of half the size, non-nil only when growing
// 分流次数,成倍扩容分流操作计数的字段
nevacuate uintptr // progress counter for evacuation (buckets less than this have been evacuated)
// 溢出桶结构,正常桶里面某个bmap存满了,会使用这里面的内存空间存放键值对
extra *mapextra // optional fields
}  

初始化了map结构后,才能分配map所使用的内存。

3.3、nil赋值

赋值 nil 之后,仅仅是把这 8 字节的指针置 0 。

3.4、nil判断

判断这指针是否为0

4、interface

4.1、变量定义

 // 定义一个接口
type Reader interface {
    Read(p []byte) (n int, err error)
}
// 定义一个接口变量
var reader Reader
// 空接口
var empty interface{}  

4.2、变量本身

 type iface struct {
tab  *itab
data unsafe.Pointer
}

type eface struct {
_type *_type
data  unsafe.Pointer
}  

其中iface是通常定义的interface类型,eface是空接口对应的数据结构,这两个结构体占用内存都是16字节。

4.3、nil赋值

赋值 nil 之后,把这 16 字节的内存块置 0 。

4.4、nil判断

需要判断类型和值

5、函数

5.1、变量定义

 var f func(int) error  

5.2、变量本身

变量本身是8字节的指针

5.3、nil赋值

本身就是指针,只不过指向的是函数而已,所以赋值也是将这 8 字节置 0 。

5.4、nil判断

判断这8个字节是否为0

6、slice

6.1、变量定义

 // 定义
var slice1 []int
var slice2 []int = []byte{1,2,3}
// 定义及初始化
var slice3 = make([]int, 3)  

var和make这两种方式有什么区别?

  • 第一种var的方式定义变量,如果逃逸分析之后,可以确认分配在栈上,那么就在栈上分配24个字节,如果逃逸到堆上,那么调用newobject函数进行类型分析。
  • 第二种make方式略有不同,如果逃逸分析之后,确认分配在栈上,那么直接在栈上分配24字节,如果逃逸到堆上,会调用makeslice来分配变量。

6.2、变量本身

 type slice struct {
array unsafe.Pointer// 管理的内存块首地址
len   int// 动态数组实际使用大小
cap   int// 动态数据内存大小
}  

变量本身占用24字节。

6.3、nil赋值

本身的 24 字节的内存块被置 0。

6.4、nil判断

那么什么样的slice被认为是nil?

指针为0,也就是这个动态数组没有实际数据的时候。

问题:仅判断指针?对len和cap两个字段不做判断吗?

 package main

import "unsafe"

type sliceType struct {
pdata unsafe.Pointer
len int
cap int
}

func main() {
var slice []byte
((*sliceType)(unsafe.Pointer(&slice))).len = 0x3
((*sliceType)(unsafe.Pointer(&slice))).cap = 0x4

if slice != nil {
println("not nil")
} else {
println("nil")
}
}

// Output: nil  

三、总结

1、变量就是绑定到某个内存块上的名称

2、变量定义分配的内存是置零分配的

3、不是所有的类型能够赋值 nil,并且和 nil 进行对比判断。只有 指针、channel、函数、interface、map、slice 这 6 种类型

4、channel、map类型的变量需要make才能使用

5、slice在声明后可以使用,是因为struct slice核心结构在定义的时候就已经分配出来了。

6、slice是24字节,interface是16字节,其他的都是8字节

7、这 6 种类型和 nil 进行比较判断本质上都是和变量本身做判断,slice 是判断管理结构的第一个指针字段,map,channel 本身就是指针

文章来源:智云一二三科技

文章标题:Go里的nil

文章地址:https://www.zhihuclub.com/86914.shtml

关于作者: 智云科技

热门文章

网站地图