亚洲乱码中文字幕综合,中国熟女仑乱hd,亚洲精品乱拍国产一区二区三区,一本大道卡一卡二卡三乱码全集资源,又粗又黄又硬又爽的免费视频

GO語言中ni,零值與空結(jié)構(gòu)體的使用

 更新時(shí)間:2024年10月16日 10:16:29   作者:飛空之羽  
Go語言為Java開發(fā)者帶來了一些新概念,如零值、nil和空結(jié)構(gòu)體,理解這些概念有助于Go語言的學(xué)習(xí)和應(yīng)用,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧

go語言的初學(xué)者,特別是java開發(fā)者新學(xué)習(xí)go語言,對(duì)于一些和java類似但是又有差異的概念很容易混淆,比如說go中的零值,nil 和 空結(jié)構(gòu)體。本文就來詳細(xì)探討一下go中這些特殊概念的含義和實(shí)際場(chǎng)景中的應(yīng)用:

零值

零值(The Zero Value)可以看作為當(dāng)你聲明了一個(gè)變量,但沒有顯式的初始化的時(shí)候,系統(tǒng)為變量賦予的一個(gè)默認(rèn)初始值。官方對(duì)零值的定義如下:

When storage is allocated for a variable, either through a declaration or a call of new, or when a new value is created, either through a composite literal or a call of make, and no explicit initialization is provided, the variable or value is given a default value. Each element of such a variable or value is set to the zero value for its type: false for booleans, 0 for numeric types, “” for strings, and nil for pointers, functions, interfaces, slices, channels, and maps. This initialization is done recursively, so for instance each element of an array of structs will have its fields zeroed if no value is specified.

據(jù)此我們可總結(jié)出:

  • 值類型 布爾類型為 false, 數(shù)值類型為 0,字符串為”“,數(shù)組和結(jié)構(gòu)體(struct)會(huì)遞歸初始化其元素或字段,即其初始值取決于元素或字段。這里所謂的值類型其實(shí)就相當(dāng)于java中的 primary 類型,只是需要注意的是string在java中是對(duì)象類型,而go中string則是值類型。

  • 引用類型 均為 nil,包括指針 pointer,函數(shù) function,接口 interface,切片 slice,管道 channel,映射 map。

tip: 其實(shí)go里面沒有真正的引用類型,可以粗略的理解為值類型的變量直接存儲(chǔ)值,引用類型的變量存儲(chǔ)的是一個(gè)地址,這個(gè)地址用于存儲(chǔ)最終的值

值類型

因?yàn)橛辛阒档拇嬖?,使得我們?cè)谑褂米兞繒r(shí),大部分情況下可以不必進(jìn)行初始化而直接使用,這樣能夠保持代碼的簡(jiǎn)潔性,也能夠盡量避免出現(xiàn)Java開發(fā)中常見的**NullPointerException,**以下是一些例子:

package main

import "sync"

type Value struct {
    mu sync.Mutex   //無需初始化,聲明就能用
    val int
}

func (v *Value)Incr(){
    defer v.mu.Unlock()
    v.mu.Lock()
    v.val++
}

func main() {
    var i Value
    i.Incr()
}

sync.Mutex本質(zhì)上是一個(gè)結(jié)構(gòu)體:

// A Mutex is a mutual exclusion lock.
// The zero value for a Mutex is an unlocked mutex.
//
// A Mutex must not be copied after first use.
type Mutex struct {
	state int32
	sema  uint32
}

那么如果是引用類型,零值為nil,是不是就不能直接用了呢?這個(gè)實(shí)際上也要分情況,按照類型我們一個(gè)個(gè)來看:

切片(Slices)

切片的零值是一個(gè)nil slice,除了不能按照序號(hào)索引查詢以外,其它的操作都能做:

func testNilSlice() {
	var nilSlice []string
    fmt.Println(nilSlice == nil) // true
    fmt.Println(nilSlice[0]) //index out of range
	emptySlice = append(nilSlice, "dd") // append操作會(huì)自動(dòng)擴(kuò)容
	fmt.Println(nilSlice[0]) //輸出dd
}

nil slice與not nil slice的區(qū)別:

type Person {
  Friends []string
}

var f1 []string //nil切片
json1, _ := json.Marshal(Person{Friends: f1})
fmt.Printf("%s\n", json1) //output:{"Friends": null}

f2 := make([]string, 0) //non-nil空切片 ,等價(jià)于 f2 := []string{}
json2, _ := json.Marshal(Person{Friends: f2})
fmt.Printf("%s\n", json2) //output: {"Friends": []}

推薦在日常使用時(shí),沒有特殊需求都使用var nilSlice []string 這樣的形式聲明空切片:https://github.com/golang/go/wiki/CodeReviewComments#declaring-empty-slices

Map

對(duì)于nil的map,我們可以簡(jiǎn)單把它看成是一個(gè)只讀的map,不能進(jìn)行寫操作,否則就會(huì)panic:

func testNilMap() {
	var m map[string]string
	fmt.Println(m["key"]) //輸出""
    m["key"]="value" //panic: assignment to entry in nil map
}

那么nil map有啥用呢,可以看看以下的例子:

func NewGet(url string, headers map[string]string) (*http.Request, error) {
	req, err := http.NewRequest(http.MethodGet, url, nil)
	if err != nil {
		return nil, err
	}
	for k, v := range headers {
		req.Header.Set(k, v)
	}
	return req, nil
}

//調(diào)用該方法時(shí)如果沒有header,可以傳入一個(gè)空的map,例如:
NewGet("http://google.com", map[string]string{})
//也可以直接傳入nil
NewGet("http://google.com", nil)

Channel

nil channel會(huì)阻塞對(duì)該channel的所有讀、寫。所以,可以將某個(gè)channel設(shè)置為nil,進(jìn)行強(qiáng)制阻塞,對(duì)于select分支來說,就是強(qiáng)制禁用此分支

func addIntegers(c chan int) {
    sum := 0
    t := time.NewTimer(time.Second)
    for {
        select {
            case input := <-c:
                sum = sum + input
            case <-t.C:
                c = nil
                fmt.Println(sum)  // 輸出10
        }
    }
}

func main() {
	c := make(chan int, 1)
	go addIntegers(c)
	for i := 0; i <= 10; i++ {
		c <- i
		time.Sleep(time.Duration(200) * time.Millisecond)
	}
}

指針(Pointers)

指針如果為nil,則對(duì)指針進(jìn)行解引用的話,會(huì)引發(fā)我們?cè)趈ava中非常熟悉的空指針錯(cuò)誤

type Person struct {
	Name string
	Sex  string
	Age  int
}

var p *Person
fmt.Println(p.Name)  // panic: runtime error: invalid memory address or nil pointer dereference

神奇的nil

nil 是 Golang 中預(yù)先聲明的標(biāo)識(shí)符,其主要用來表示引用類型的零值(指針,接口,函數(shù),映射,切片和通道),表示它們未初始化的值。

// [src/builtin/builtin.go](https://golang.org/src/builtin/builtin.go#L98)
//
// 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

nil在go語言里面不是一個(gè)關(guān)鍵字或者保留字,所以你可以用nil作為變量名(作死):

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

nil沒有默認(rèn)的類型,所以不能給一個(gè)未聲明類型的變量賦值,也不能和自己比較:

a := nil
// cannot declare variable as untyped nil: a
fmt.Println(nil == nil)
// invalid operation: nil == nil (operator == not defined on nil)
fmt.Printf("%T", nil)
// use of untyped nil

比較nil時(shí)一定要注意nil實(shí)際上是有類型的,不同類型的nil是不相等的,比如下面的例子:

var p *int
var i interface{}

fmt.Println(p)      // <nil>
fmt.Println(i)      // <nil>

fmt.Println(p == i) // false

再看一個(gè)在實(shí)際編碼里面很容易犯的錯(cuò)誤:

type BusinessError struct {
	error
	errorCode int64
}

func doBusiness() *BusinessError {
	return nil
}

func wrapDoBusiness() error {
	err := doBusiness()
	return err
}

func testError() {
	err := wrapDoBusiness() //這里面拿到的本質(zhì)上是一個(gè)<T:*BusinessError,V:nil>的nil
	fmt.Println(err == nil)
}

建議:如果任何地方有判斷interface是否為 nil 值的邏輯,一定不要寫任何有關(guān)于將interface賦值為具體實(shí)現(xiàn)類型(可能為nil)的代碼,如果是 nil 值就直接賦給interface,而不要過具體類型的轉(zhuǎn)換

type BusinessError struct {
	error
	errorCode int64
}

func doBusiness() *BusinessError {
	return nil
}

func wrapDoBusiness() error {
	err := doBusiness() 
	if err == nil {
		return nil  //如果返回值為nil,直接返回nil,不要做類型轉(zhuǎn)換
	} else {
		return err
	}
}

func testError() {
	err := wrapDoBusiness()
	fmt.Println(err == nil)
}

空結(jié)構(gòu)體

golang 正常的 struct 就是普通的一個(gè)內(nèi)存塊,必定是占用一小塊內(nèi)存的,并且結(jié)構(gòu)體的大小是要經(jīng)過邊界,長(zhǎng)度的對(duì)齊的,但是“空結(jié)構(gòu)體”是不占內(nèi)存的,size 為 0;

var q struct{}
fmt.Println(unsafe.Sizeof(q)) // 0

空結(jié)構(gòu)體 struct{ } 為什么會(huì)存在的核心理由就是為了節(jié)省內(nèi)存。當(dāng)你需要一個(gè)結(jié)構(gòu)體,但是卻絲毫不關(guān)系里面的內(nèi)容,那么就可以考慮空結(jié)構(gòu)體。以下是幾個(gè)經(jīng)典的用法:

map & struct{}

map 和 struct {} 一般的結(jié)合姿勢(shì)是這樣的:

// 創(chuàng)建 map
m := make(map[int]struct{})
// 賦值
m[1] = struct{}{}
// 判斷 key 鍵存不存在
_, ok := m[1]

一般 map 和 struct {} 的結(jié)合使用場(chǎng)景是:只關(guān)心 key,不關(guān)注值。比如查詢 key 是否存在就可以用這個(gè)數(shù)據(jù)結(jié)構(gòu),通過 ok 的值來判斷這個(gè)鍵是否存在,map 的查詢復(fù)雜度是 O(1) 的,查詢很快。這種方式在部分場(chǎng)景下可以起到類似Java中Set的作用

chan & struct{}

channel 和 struct{} 結(jié)合是一個(gè)最經(jīng)典的場(chǎng)景,struct{} 通常作為一個(gè)信號(hào)來傳輸,并不關(guān)注其中內(nèi)容。chan 本質(zhì)的數(shù)據(jù)結(jié)構(gòu)是一個(gè)管理結(jié)構(gòu)加上一個(gè) ringbuffer ,如果 struct{} 作為元素的話,ringbuffer 就是 0 分配的。

chan 和 struct{} 結(jié)合基本只有一種用法,就是信號(hào)傳遞,空結(jié)構(gòu)體本身攜帶不了值,所以也只有這一種用法啦,一般來說,配合 no buffer 的 channel 使用。

waitc := make(chan struct{})

// ...
goroutine 1:
    // 發(fā)送信號(hào): 投遞元素
    waitc <- struct{}
    // 發(fā)送信號(hào): 關(guān)閉
    close(waitc)

goroutine 2:
    select {
    // 收到信號(hào),做出對(duì)應(yīng)的動(dòng)作
    case <-waitc:
    }

到此這篇關(guān)于GO語言中ni,零值與空結(jié)構(gòu)體的文章就介紹到這了,更多相關(guān)GO ni 零值 空結(jié)構(gòu)體內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家! 

相關(guān)文章

  • Go語言中的空值(nil)與零值(zerovalue)區(qū)別詳解

    Go語言中的空值(nil)與零值(zerovalue)區(qū)別詳解

    在Go語言中,空值(nil)和零值(zero value)是兩個(gè)不同的概念,它們?cè)谡Z義、使用場(chǎng)景以及實(shí)際的編程實(shí)踐中有著明顯的區(qū)別,理解這兩者的差異對(duì)于編寫清晰、健壯的Go代碼至關(guān)重要,需要的朋友可以參考下
    2024-06-06
  • 詳解Go語言中new和make關(guān)鍵字的區(qū)別

    詳解Go語言中new和make關(guān)鍵字的區(qū)別

    本篇文章來介紹一道非常常見的面試題,到底有多常見呢?可能很多面試的開場(chǎng)白就是由此開始的。那就是 new 和 make 這兩個(gè)內(nèi)置函數(shù)的區(qū)別,希望對(duì)大家有所幫助
    2023-03-03
  • Go語言入門學(xué)習(xí)之Channel通道詳解

    Go語言入門學(xué)習(xí)之Channel通道詳解

    go routine可以使用channel來進(jìn)行通信,使用通信的手段來共享內(nèi)存,下面這篇文章主要給大家介紹了關(guān)于Go語言入門學(xué)習(xí)之Channel通道的相關(guān)資料,文中通過示例代碼介紹的非常詳細(xì),需要的朋友可以參考下
    2022-07-07
  • Go?語言簡(jiǎn)單實(shí)現(xiàn)Vigenere加密算法

    Go?語言簡(jiǎn)單實(shí)現(xiàn)Vigenere加密算法

    這篇文章主要介紹了Go語言簡(jiǎn)單實(shí)現(xiàn)Vigenere加密算法,文章圍繞主題展開詳細(xì)的內(nèi)容介紹,具有一定的參考價(jià)值,需要的朋友可以參考一下
    2022-09-09
  • Go并發(fā)編程實(shí)踐

    Go并發(fā)編程實(shí)踐

    并發(fā)編程一直是Golang區(qū)別與其他語言的很大優(yōu)勢(shì),也是實(shí)際工作場(chǎng)景中經(jīng)常遇到的。近日筆者在組內(nèi)分享了我們常見的并發(fā)場(chǎng)景,及代碼示例,以期望大家能在遇到相同場(chǎng)景下,能快速的想到解決方案,或者是拿這些方案與自己實(shí)現(xiàn)的比較,取長(zhǎng)補(bǔ)短?,F(xiàn)整理出來與大家共享
    2017-01-01
  • 從零封裝Gin框架配置初始化全局變量

    從零封裝Gin框架配置初始化全局變量

    這篇文章主要為大家介紹了從零封裝Gin框架配置初始化全局變量,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2024-01-01
  • 詳解Golang中Channel的用法

    詳解Golang中Channel的用法

    如果說goroutine是Go語言程序的并發(fā)體的話,那么channels則是它們之間的通信機(jī)制。這篇文章主要介紹Golang中Channel的用法,需要的朋友可以參考下
    2020-11-11
  • 使用go gin來操作cookie的講解

    使用go gin來操作cookie的講解

    今天小編就為大家分享一篇關(guān)于使用go gin來操作cookie的講解,小編覺得內(nèi)容挺不錯(cuò)的,現(xiàn)在分享給大家,具有很好的參考價(jià)值,需要的朋友一起跟隨小編來看看吧
    2019-04-04
  • 基于Go?goroutine實(shí)現(xiàn)一個(gè)簡(jiǎn)單的聊天服務(wù)

    基于Go?goroutine實(shí)現(xiàn)一個(gè)簡(jiǎn)單的聊天服務(wù)

    對(duì)于聊天服務(wù),想必大家都不會(huì)陌生,因?yàn)樵谖覀兊纳钪薪?jīng)常會(huì)用到,本文我們用?Go?并發(fā)來實(shí)現(xiàn)一個(gè)聊天服務(wù)器,這個(gè)程序可以讓一些用戶通過服務(wù)器向其它所有用戶廣播文本消息,文中通過代碼示例介紹的非常詳細(xì),需要的朋友可以參考下
    2023-06-06
  • golang 中signal包的Notify用法說明

    golang 中signal包的Notify用法說明

    這篇文章主要介紹了golang 中signal包的Notify用法說明,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過來看看吧
    2021-03-03

最新評(píng)論