Go 學習筆記(28)— nil(nil 不能比較、不是關鍵字或保留字、nil 沒有默認類型、不同類型的 nil 指針是一樣的、不同類型的 nil 是不能比較的、相同類型的 nil 可能也無法比較)

Go 語言中,布爾類型的零值(初始值)爲 false ,數值類型的零值爲 0,字符串類型的零值爲空字符串 "",而指針、切片、映射、通道、函數和接口的零值則是 nil

nilGo 語言中一個預定義好的標識符,有過其他編程語言開發經驗的開發者也許會把 nil 看作其他語言中的 nullNULL ),其實這並不是完全正確的,因爲 Go 語言中的 nil 和其他語言中的 null 有很多不同點。

下面通過幾個方面來介紹一下 Go 語言中 nil

1. nil 標識符是不能比較的

package main

import (
	"fmt"
)

func main() {
	fmt.Println(nil == nil)
}

輸出結果:

# command-line-arguments
./hello.go:8:18: invalid operation: nil == nil (operator == not defined on nil)

這點和 python 等動態語言是不同的,在 python 中,兩個 None 值永遠相等。

>>> None == None
True
>>> 

從上面的運行結果不難看出, == 對於 nil 來說是一種未定義的操作。

2. nil 不是關鍵字或保留字

nil 並不是 Go 語言的關鍵字或者保留字,也就是說我們可以定義一個名稱爲 nil 的變量,比如下面這樣:

var nil = errors.New("my god")

雖然上面的聲明語句可以通過編譯,但是並不提倡這麼做。

3. nil 沒有默認類型

package main

import (
	"fmt"
)

func main() {
	fmt.Printf("%T", nil)
	print(nil)
}

輸出結果:

# command-line-arguments
./hello.go:9:7: use of untyped nil

4. 不同類型 nil 的指針是一樣的

package main

import (
    "fmt"
)

func main() {
    var arr []int
    var num *int
    fmt.Printf("%p\n", arr)
    fmt.Printf("%p", num)
}

輸出結果:

0x0
0x0

通過運行結果可以看出 arrnum 的指針都是 0x0。

5. 不同類型的 nil 是不能比較的

package main

import (
	"fmt"
)

func main() {
	var m map[int]string
	var ptr *int
	fmt.Printf(m == ptr)
}

輸出結果:

# command-line-arguments
./hello.go:10:15: invalid operation: m == ptr (mismatched types map[int]string and *int)

6. 兩個相同類型的 nil 值也可能無法比較

Go 語言中 mapslicefunction 類型的 nil 值不能比較,比較兩個無法比較類型的值是非法的,下面的語句無法編譯。

package main

import (
	"fmt"
)

func main() {
	var s1 []int
	var s2 []int
	fmt.Printf(s1 == s2)
}

輸出結果:

# command-line-arguments
./hello.go:10:16: invalid operation: s1 == s2 (slice can only be compared to nil)

通過上面的錯誤提示可以看出,能夠將上述不可比較類型的空值直接與 nil 標識符進行比較,如下所示:

package main

import (
	"fmt"
)

func main() {
	var s1 []int
	fmt.Println(s1 == nil)	// true
}

7. nil 是 map、slice、pointer、channel、func、interface 的零值

package main

import (
	"fmt"
)

func main() {
	var m map[int]string
	var ptr *int
	var c chan int
	var sl []int
	var f func()
	var i interface{}
	fmt.Printf("%#v\n", m)   // map[int]string(nil)
	fmt.Printf("%#v\n", ptr) // (*int)(nil)
	fmt.Printf("%#v\n", c)   // (chan int)(nil)
	fmt.Printf("%#v\n", sl)  // []int(nil)
	fmt.Printf("%#v\n", f)   // (func())(nil)
	fmt.Printf("%#v\n", i)   // <nil>
}

零值是 Go 語言中變量在聲明之後但是未初始化被賦予的該類型的一個默認值。

8. 不同類型的 nil 值佔用的內存大小可能是不一樣的

一個類型的所有的值的內存佈局都是一樣的, nil 也不例外, nil 的大小與同類型中的非 nil 類型的大小是一樣的。但是不同類型的 nil 值的大小可能不同。

package main

import (
	"fmt"
	"unsafe"
)

func main() {
	var p *struct{}
	fmt.Println(unsafe.Sizeof(p)) // 8

	var s []int
	fmt.Println(unsafe.Sizeof(s)) // 24

	var m map[int]bool
	fmt.Println(unsafe.Sizeof(m)) // 8

	var c chan string
	fmt.Println(unsafe.Sizeof(c)) // 8

	var f func()
	fmt.Println(unsafe.Sizeof(f)) // 8

	var i interface{}
	fmt.Println(unsafe.Sizeof(i)) // 16
}

具體的大小取決於編譯器和架構,上面打印的結果是在 64 位架構和標準編譯器下完成的,對應 32 位的架構的,打印的大小將減半。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章