<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          你不知道的 Go 之 const

          共 8357字,需瀏覽 17分鐘

           ·

          2021-06-08 08:16

          簡(jiǎn)介

          常量可以說(shuō)在每個(gè)代碼文件中都存在,使用常量有很多好處:

          • 避免魔法字面量,即直接出現(xiàn)在代碼中的數(shù)字,字符串等。閱讀代碼的時(shí)候無(wú)法一眼看出它的含義。另外可以避免使用字面量可能出現(xiàn)的不一致,當(dāng)它們的值需要修改時(shí),常量只需修改一處,而字面量要修改多處,容易遺漏造成不一致;
          • 相對(duì)于變量,常量可以執(zhí)行編譯期優(yōu)化。

          Go 語(yǔ)言也提供了常量的語(yǔ)法支持,與其他語(yǔ)言提供的常量基本一致。但是 Go 中的常量有幾個(gè)有用的特性值得了解一下。

          常量基礎(chǔ)

          Go 語(yǔ)言中使用const關(guān)鍵字定義常量:

          package main

          import "fmt"

          const PI float64 = 3.1415926
          const MaxAge int = 150
          const Greeting string = "hello world"

          func main() {
            fmt.Println(PI)
            fmt.Println(MaxAge)
            fmt.Println(Greeting)
          }

          多個(gè)常量定義可以合并在一起,如上面的幾個(gè)常量定義可以寫成下面的形式:

          const (
            PI       float64 = 3.1415926
            MaxAge   int     = 150
            Greeting string  = "hello world"
          )

          不過(guò)通常建議將相同類型的,相關(guān)聯(lián)的常量定義在一個(gè)組里面。

          Go 語(yǔ)言中常量有一個(gè)很大的限制:只能定義基本類型的常量,即布爾類型(bool),整數(shù)(無(wú)符號(hào)uint/uint8/uint16/uint32/uint64/uintptr,有符號(hào)int/int8/int16/int32/int64),浮點(diǎn)數(shù)(單精度float32,雙精度float64),或者底層類型是這些基本類型的類型。不能定義切片,數(shù)組,指針,結(jié)構(gòu)體等這些類型的常量。例如,byte底層類型為uint8,rune底層類型為int32,見(jiàn) Go 源碼builtin.go

          // src/builtin/builtin.go
          type byte = uint8
          type rune = int32

          故可以定義類為byterune的常量:

          const b byte = 128
          const r rune = 'c'

          定義其他類型的變量會(huì)在編譯期報(bào)錯(cuò):

          type User struct {
            Name string
            Age  int
          }

          const u User = User{} // invalid const type User

          var i int = 1
          const p *int = &i // invalid const type *int

          iota

          Go 語(yǔ)言的代碼中常量定義經(jīng)常使用iota,下面看幾個(gè) Go 的源碼。

          標(biāo)準(zhǔn)庫(kù)time源碼:

          // src/time/time.go
          type Month int

          const (
            January Month = 1 + iota
            February
            March
            April
            May
            June
            July
            August
            September
            October
            November
            December
          )

          type Weekday int

          const (
            Sunday Weekday = iota
            Monday
            Tuesday
            Wednesday
            Thursday
            Friday
            Saturday
          )

          標(biāo)準(zhǔn)庫(kù)net/http源碼:

          // src/net/http/server.go
          type ConnState int

          const (
            StateNew ConnState = iota
            StateActive
            StateIdle
            StateHijacked
            StateClosed
          )

          iota是方便我們定義常量的一個(gè)機(jī)制。簡(jiǎn)單來(lái)說(shuō),iota獨(dú)立作用于每一個(gè)常量定義組中(單獨(dú)出現(xiàn)的每個(gè)const語(yǔ)句都算作一個(gè)組),iota出現(xiàn)在用于初始化常量值的常量表達(dá)式中,iota的值為它在常量組中的第幾行(從 0 開(kāi)始)。使用iota定義的常量下面可以省略類型和初始化表達(dá)式,這時(shí)會(huì)沿用上一個(gè)定義的類型和初始化表達(dá)式。我們看幾組例子:

          const (
            One int = iota + 1
            Two
            Three
            Four
            Five
          )

          這個(gè)也是最常使用的方式,iota出現(xiàn)在第幾行,它的值就是多少。上面常量定義組中,One在第 0 行(注意從 0 開(kāi)始計(jì)數(shù)),iota為 0,所以One = 0 + 1 = 1。下一行Two省略了類型和初始化表達(dá)式,因此Two沿用上面的類型int,初始化表達(dá)式也是iota + 1。但是此時(shí)是定義組中的第 1 行,iota的值為 1,所以Two = 1 + 1 = 2。再下一行Three也省略了類型和初始化表達(dá)式,因此Three沿用了Two進(jìn)而沿用了One的類型int,初始化表達(dá)式也是iota + 1,但是此時(shí)是定義的第 2 行,所以Three = 2 + 1 = 3。以此類推。

          我們可以在非常復(fù)雜的初始化表達(dá)式中使用iota

          const (
            Mask1 int = 1<<(iota+1) - 1
            Mask2
            Mask3
            Mask4
          )

          按照上面的分析Mask1~4依次為 1, 3, 7, 15。

          另外還有奇數(shù),偶數(shù):

          const (
            Odd1 = 2*iota + 1
            Odd2
            Odd3
          )

          const (
            Even1 = 2 * (iota + 1)
            Even2
            Even3
          )

          在一個(gè)組中,iota不一定出現(xiàn)在第 0 行,但是它出現(xiàn)在第幾行,值就為多少

          const (
            A int = 1
            B int = 2
            C int = iota + 1
            D
            E
          )

          上面iota出現(xiàn)在第 2 行(從 0 開(kāi)始),C的值為2 + 1 = 3。DE分別為 4, 5。

          一定要注意iota的值等于它出現(xiàn)在組中的第幾行,而非它的第幾次出現(xiàn)。

          可以通過(guò)賦值給空標(biāo)識(shí)符來(lái)忽略值:

          const (
            _ int = iota
            A // 1
            B // 2
            C // 3
            D // 4
            E // 5
          )

          說(shuō)了這么多iota的用法,那么為什么要用iota呢?換句話說(shuō),iota有什么優(yōu)點(diǎn)?我覺(jué)得有兩點(diǎn):

          • 方便定義,在模式比較固定的時(shí)候,我們可以只寫出第一個(gè),后面的常量不需要寫出類型和初始化表達(dá)式了;
          • 方便調(diào)整順序,增加和刪除常量定義,如果我們定義了一組常量之后,想要調(diào)整順序,使用iota的定義,只需要調(diào)整位置即可,不需要修改初始化式,因?yàn)榫蜎](méi)有寫。增加和刪除也是一樣的,如果我們一個(gè)個(gè)寫出了初始化式,刪除中間某個(gè),后續(xù)的值就必須做調(diào)整。

          例如,net/http中的源碼:

          type ConnState int

          const (
            StateNew ConnState = iota
            StateActive
            StateIdle
            StateHijacked
            StateClosed
          )

          如果我們需要增加一個(gè)常量,表示正在關(guān)閉的狀態(tài)?,F(xiàn)在只需要寫出新增的狀態(tài)名:

          type ConnState int

          const (
            StateNew ConnState = iota
            StateActive
            StateIdle
            StateHijacked
            StateClosing // 新增的狀態(tài)
            StateClosed
          )

          如果是顯式寫出初始化式:

          type ConnState int

          const (
            StateNew ConnState = 0
            StateActive ConnState = 1
            StateIdle ConnState = 2
            StateHijacked ConnState = 3
            StateClosed ConnState = 4
          )

          這時(shí)新增需要改動(dòng)后續(xù)的值。另外需要鍵入的字符也多了不少??:

          const (
            StateNew ConnState = 0
            StateActive ConnState = 1
            StateIdle ConnState = 2
            StateHijacked ConnState = 3
            StateClosing ConnState = 4
            StateClosed ConnState = 5
          )

          無(wú)類型常量

          Go 語(yǔ)言中有一種特殊的常量,即無(wú)類型常量。即在定義時(shí),我們不顯式指定類型。這種常量可以存儲(chǔ)超過(guò)常規(guī)的類型范圍的值:

          package main

          import (
            "fmt"
            "math"
            "reflect"
          )

          const (
            Integer1 = 1000
            Integer2 = math.MaxUint64 + 1
            Float1   = 1.23
            Float2   = 1e100
            Float3   = 1e400
          )

          func main() {
            fmt.Println("integer1=", Integer1, "type", reflect.TypeOf(Integer1).Name())
            // 編譯錯(cuò)誤
            // fmt.Println("integer2=", Integer2, "type", reflect.TypeOf(Integer2).Name())
            fmt.Println("integer2/10=", Integer2/10"type", reflect.TypeOf(Integer2/10).Name())

            fmt.Println("float1=", Float1, "type", reflect.TypeOf(Float1).Name())
            fmt.Println("float2=", Float2, "type", reflect.TypeOf(Float2).Name())
            // 編譯錯(cuò)誤
            // fmt.Println("float3=", Float3, "type", reflect.TypeOf(Float3).Name())
            fmt.Println("float3/float2=", Float3/Float2, "type", reflect.TypeOf(Float3/Float2).Name())
          }

          雖然無(wú)類型常量可以存儲(chǔ)超出正常類型范圍的值,并且可以相互之間做算術(shù)運(yùn)算,但是它在使用時(shí)(賦值給變量,作為參數(shù)傳遞)還是需要轉(zhuǎn)回正常類型。如果值超過(guò)正常類型的范圍,編譯就會(huì)報(bào)錯(cuò)。每個(gè)無(wú)類型常量都有一個(gè)默認(rèn)類型,整數(shù)的默認(rèn)類型為int,浮點(diǎn)數(shù)(有小數(shù)點(diǎn)或者使用科學(xué)計(jì)數(shù)法表示的都被當(dāng)成浮點(diǎn)數(shù))的默認(rèn)類型為float64。所以上面例子中,我們定義Integer2為無(wú)類型常量,值為uint64的最大值 + 1,這是允許的。但是如果我們直接輸出Integer2的值,就會(huì)導(dǎo)致編譯報(bào)錯(cuò),因?yàn)?code style="font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;background-color: rgba(27, 31, 35, 0.05);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(239, 112, 96);">Integer2默認(rèn)會(huì)轉(zhuǎn)為int類型,而它存儲(chǔ)的值超過(guò)了int的范圍了。另一方面,我們可以用Integer2做運(yùn)算,例如除以 10,得到的值在int范圍內(nèi),可以輸出。(我使用的是 64 位機(jī)器)

          下面的浮點(diǎn)數(shù)類型也是類似的,Float3超出了float64的表示范圍,故不能直接輸出。但是Float3/Float2的結(jié)果在float64的范圍內(nèi),可以使用。

          上面程序輸出:

          integer1= 1000 type int
          integer2/101844674407370955161 type int
          float1= 1.23 type float64
          float2= 1e+100 type float64
          float3/float2= 1e+300 type float64

          由輸出也可以看出整數(shù)和浮點(diǎn)的默認(rèn)類型分別為intfloat64。

          結(jié)合iota和無(wú)類型常量我們可以定義一組存儲(chǔ)單位:

          package main

          import "fmt"

          const (
            _  = iota
            KB = 1 << (10 * iota)
            MB // 2 ^ 20
            GB // 2 ^ 30
            TB // 2 ^ 40
            PB // 2 ^ 50
            EB // 2 ^ 60
            ZB // 2 ^ 70,1180591620717411303424
            YB // 2 ^ 80
          )

          func main() {
            fmt.Println(YB / ZB)
            fmt.Println("1180591620717411303424 B = "1180591620717411303424/ZB, "ZB")
          }

          ZB實(shí)際上已經(jīng)達(dá)到 1180591620717411303424,超過(guò)了int的表示范圍了,但是我們?nèi)匀豢梢远xZBYB,還能在使用時(shí)對(duì)他們進(jìn)行運(yùn)算,只要最終要使用的值在正常類型的范圍內(nèi)即可。

          總結(jié)

          本文介紹了常量的相關(guān)知識(shí),記住兩個(gè)要點(diǎn)即可:

          • iota的值等于它出現(xiàn)在常量定義組的第幾行(從 0 開(kāi)始);
          • 無(wú)類型常量可以定義超過(guò)存儲(chǔ)范圍的值,但是使用時(shí)必須能轉(zhuǎn)回正常類型的范圍,否則會(huì)報(bào)錯(cuò)。

          利用無(wú)類型常量,我們可以在編譯期對(duì)大數(shù)進(jìn)行算術(shù)運(yùn)算。

          參考

          1. 《Go 程序設(shè)計(jì)語(yǔ)言》
          2. 你不知道的Go GitHub:https://github.com/darjun/you-dont-know-go


          推薦閱讀


          福利

          我為大家整理了一份從入門到進(jìn)階的Go學(xué)習(xí)資料禮包,包含學(xué)習(xí)建議:入門看什么,進(jìn)階看什么。關(guān)注公眾號(hào) 「polarisxu」,回復(fù) ebook 獲??;還可以回復(fù)「進(jìn)群」,和數(shù)萬(wàn) Gopher 交流學(xué)習(xí)。

          瀏覽 43
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          評(píng)論
          圖片
          表情
          推薦
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  青娱乐在线免费视频 | 色婷婷五月天在线观看 | 大香蕉中文电影 | 撸撸撸在线观看 | 欧美人操B视频免费观看 |