首頁 > 軟體

詳解Go語言各種常見型別的預設值和判空方法

2023-11-26 14:00:12

起因(解決的問題)

由於在專案中設計到了型別的判空,所以突然好奇起來,每個型別如果只是宣告,而沒有初始化,那麼預設值是多少?怎麼判斷它是不是空值?所以去整理了一下

基本型別的預設值

1.常見的基本資料型別有:資料型別(int,uint,float之類的),字串(string),結構體,陣列,指標。

2.那麼他們的預設值是:

資料型別

預設值

int

0

float

0.00000

string

“”

結構體

根據結構體內部的基礎資料型別進行初始化賦值,下面會有demo

陣列(切片)

空陣列

指標

nil

3.例子:

package main
 
import (
    "fmt"
)
 
type UserInfo struct {
    Name string
    Age  int
    Sex  string
    Flag bool
}
 
// main函數
func main() {
    PrintDefault()
}
 
// 輸出預設值的函數
func PrintDefault() {
    var a int
    var b bool
    var c float64
    var d byte
    var e string
    var f UserInfo
    var g *UserInfo
    var ip *int
    var bp *bool
    var fp *float64
    var sp *string
    var ssp *byte
    var iArray []int
 
    fmt.Println("-------預設值列表--------")
    fmt.Printf("int的預設值為:%dn", a)
    fmt.Printf("bool的預設值為:%tn", b)
    fmt.Printf("float64的預設值為:%fn", c)
    fmt.Printf("byte的預設值為:%bn", d)
    fmt.Printf("string的預設值為:%sn", e)
    fmt.Printf("結構體UserInfo的預設值為:%vn", f)
    fmt.Printf("結構體指標UserInfo的預設值為:%vn", g)
    fmt.Printf("int陣列的預設值為:%vn", iArray)
    fmt.Printf("int指標的預設值為:%pn", ip)
    fmt.Printf("byte指標的預設值為:%pn", bp)
    fmt.Printf("string指標的預設值為:%pn", fp)
    fmt.Printf("float64指標的預設值為:%pn", sp)
    fmt.Printf("byte指標的預設值為:%pn", ssp)
    if ip != nil {
        fmt.Printf("string指標的預設值為:%dn", *ip)
    }
}

執行結果截圖:

由上可以知道兩個點:

1.各種資料型別怎麼輸出,對應的d%,v%,s%是什麼。(大家可以看一下,後面自己本地測試輸出紀錄檔也方便)

2.瞭解各種資料的預設值,總結來說就是:資料型別是0,字元是空字元“”,結構體指標是nil,基礎資料結構指標是0x0。

值得注意的是:雖然基礎資料型別指標的輸出和結構體指標的輸出不太一樣,但是實際判空的時候,都是視為nil的。例如:

var ip *int
if ip!=nil{//不會進入該邏輯,即:ip指向了0x0的時候,是視為nil的
        fmt.Printf("string指標的預設值為:%dn", *ip)
    }

好了,那麼瞭解了各個資料型別的預設值,判空就好做多了。

判斷是否初始化(判空)

方法1:

直接判斷它和預設值是否一樣,是的話就認為是沒有初始化的。(這部分主要是瞭解原理,實際我們開發過程用方法2好點)

package main
 
import (
    "fmt"
    "reflect"
)
 
type UserInfo struct {
    Name string
    Age  int
    Sex  string
    Flag bool
}
 
func main() {
    fmt.Println("-----------判斷型別函數實驗----------")
    var a int
    var b bool
    var c float64
    var d byte
    var e string
    var f UserInfo
    var g *UserInfo
    var ip *int
    var sp *string
    if g == nil {
        fmt.Println("nil判斷成功")
    }
    var iSlice []int
    var iArray [2]int
    CheckType(a)
    CheckType(b)
    CheckType(c)
    CheckType(d)
    CheckType(e)
    CheckType(f)
    CheckType(g)
    CheckType(ip)
    CheckType(sp)
    CheckType(iArray)
    CheckType(iSlice)
}
 
// 自己寫了一個判空函數,你可以直接看判空部分的邏輯就好了。
func CheckType(args ...interface{}) {
    for _, arg := range args {
        fmt.Printf("資料型別為:%sn", reflect.TypeOf(arg).Kind().String()) //先利用反射獲取資料型別,再進入不同型別的判空邏輯
        switch reflect.TypeOf(arg).Kind().String() {
        case "int":
            if arg == 0 {
                fmt.Println("資料為int,是空值")
            }
        case "string":
            if arg == "" {
                fmt.Println("資料為string,為空值")
            } else {
                fmt.Println("資料為string,數值為:", arg)
            }
        case "int64":
            if arg == 0 {
                fmt.Println("資料為int64,為空值")
            }
        case "uint8":
            if arg == false {
                fmt.Println("資料為bool,為false")
            }
        case "float64":
            if arg == 0.0 {
                fmt.Println("資料為float,為空值")
            }
        case "byte":
            if arg == 0 {
                fmt.Println("資料為byte,為0")
            }
        case "ptr":
            if arg == nil { //介面狀態下,它不認為自己是nil,所以要用反射判空
                fmt.Println("資料為指標,為nil")
            } else {
                fmt.Println("資料不為空,為", arg)
            }
            //反射判空邏輯
            if reflect.ValueOf(arg).IsNil() { //利用反射直接判空
                fmt.Println("反射判斷:資料為指標,為nil")
                fmt.Println("nil:", reflect.ValueOf(nil).IsValid()) //利用反射判斷是否是有效值
            }
        case "struct":
            if arg == nil {
                fmt.Println("資料為struct,為空值")
            } else {
                fmt.Println("資料為struct,預設有數,無法判空,只能判斷對應指標有沒有初始化,直接結構體無法判斷")
            }
        case "slice":
            s := reflect.ValueOf(arg)
            if s.Len() == 0 {
                fmt.Println("資料為陣列/切片,為空值")
            }
        case "array":
            s := reflect.ValueOf(arg)
            if s.Len() == 0 {
                fmt.Println("資料為陣列/切片,為空值")
            } else {
                fmt.Println("資料為陣列/切片,為", s.Len())
            }
        default:
            fmt.Println("奇怪的資料型別")
        }
    }
}

執行結果截圖:

由上可知。基本還是那句話:資料型別預設0,指標型別預設nil(介面型別下,空指標==nil會不通過,要用反射判空),字元型別為空字串“”。

方式2:

利用反射包的內建函數判空. 正如上面展示的指標判空邏輯。實際上go已經有一個反射包裡面封裝了判斷

package main
 
import (
    "fmt"
    "reflect"
)
 
type UserInfo struct {
    Name string
    Age  int
    Sex  string
    Flag bool
}
 
func main() {
    fmt.Println("-----------指標型別判空實驗----------")
    var g *UserInfo
    var ip *int
    var sp *string
    var iSlice []int
    CheckTypeByReflectNil(g)
    CheckTypeByReflectNil(ip)
    CheckTypeByReflectNil(sp)
    CheckTypeByReflectNil(iSlice)
    fmt.Println("-----------基礎型別判空實驗----------")
    var a int
    var b bool
    var c float64
    var d byte
    var e string
    var f UserInfo
    CheckTypeByReflectZero(a)
    CheckTypeByReflectZero(b)
    CheckTypeByReflectZero(c)
    CheckTypeByReflectZero(d)
    CheckTypeByReflectZero(e)
    CheckTypeByReflectZero(f)
}
 
func CheckTypeByReflectNil(arg interface{}) {
    if reflect.ValueOf(arg).IsNil() { //利用反射直接判空,指標用isNil
        // 函數解釋:isNil() bool    判斷值是否為 nil
        // 如果值型別不是通道(channel)、函數、介面、map、指標或 切片時發生 panic,類似於語言層的v== nil操作
        fmt.Printf("反射判斷:資料型別為%s,資料值為:%v,nil:%v n",
            reflect.TypeOf(arg).Kind(), reflect.ValueOf(arg), reflect.ValueOf(arg).IsValid())
    }
}
 
func CheckTypeByReflectZero(arg interface{}) {
    if reflect.ValueOf(arg).IsZero() { //利用反射直接判空,基礎資料型別用isZero
        fmt.Printf("反射判斷:資料型別為%s,資料值為:%v,nil:%v n",
            reflect.TypeOf(arg).Kind(), reflect.ValueOf(arg), reflect.ValueOf(arg).IsValid())
    }
}

執行結果截圖:

到此這篇關於詳解Go語言各種常見型別的預設值和判空方法的文章就介紹到這了,更多相關Go語言常見型別的預設值和判空內容請搜尋it145.com以前的文章或繼續瀏覽下面的相關文章希望大家以後多多支援it145.com!


IT145.com E-mail:sddin#qq.com