首頁 > 軟體

Go語言資料型別詳細介紹

2022-03-31 13:03:05

一、Go 語言兩巨量資料型別

Go 語言資料型別包含基礎型別和複合型別兩大類。

基礎型別包括:

  • 布林型別 bool。
  • 數值型別 int,int8,int16,int32,int64,float32,float64。
  • uint8,uint16,uint32,uint64。
  • 字串 string,byte,rune。

複合型別包括:

  • 指標、陣列、切片、字典、通道、結構體、介面。

下面用程式碼加註釋的方式說明:

二、基礎型別

宣告一個bool型別的變數,只有true和false兩個值,初始值為false    

var isLogin bool
    
// 宣告一個int型別的變數,初始值為0
//(int8,int16,int32 uint8,uint16....類似,只是有符號和無符號的區別)
// 最常用的就是int,如果需要更大的範圍可以用int64或者uint64
var count int
        
// 宣告一個string型別的變數,初始值為""
var s string
        
//宣告一個byte型別的變數
var b byte
    
//宣告一個rune型別的變數
//int32的別名,表示單個Unicode字元
var r rune

三、複合型別

指標

go的指標和c語言的指標型別,都是表示一個變數的地址,不同的是,go的指標要比c的指標簡單的多,老規矩,程式碼註釋,如下:

package main

import "fmt"

func main() {
    
    var count = 100 //定義變數count
    
    var ptr *int     //定義一個指標ptr,此指標可以儲存int型別變數的地址
    ptr = &count    //ptr儲存的是變數count的地址, & 符號是取變數地址的符號
    
    fmt.Println("count=",count) //列印count的值
    fmt.Println("ptr=", *ptr)    //列印ptr指向的變數的值,此句列印100
}

執行結果如下:

count= 100
ptr= 100

四、陣列(array)

陣列為一組相同資料型別資料的集合,大小固定,不能更改,每個元素稱為element,宣告的陣列元素預設值都是對應型別的0值。而且陣列在Go語言中是一個值型別(value type)所有值型別變數在賦值和作為引數傳遞時都會產生一次複製動作,即對原值的拷貝

package main

import "fmt"

func main() {
    
    // 1.宣告後賦值  (var <陣列名稱> [<陣列長度>]<陣列元素>)
    var arr [2]int   // 陣列元素的預設值都是 0
    fmt.Println(arr) // 輸出:[0 0]
    arr[0] = 1
    arr[1] = 2
    fmt.Println(arr) // 輸出:[1 2]
    
    // 2.宣告並賦值 (var <陣列名稱> = [<陣列長度>]<陣列元素>{元素1,元素2,...})
    var intArr = [2]int{1, 2}
    strArr := [3]string{`aa`, `bb`, `cc`}
    fmt.Println(intArr) // 輸出:[1 2]
    fmt.Println(strArr) // 輸出:[aa bb cc]
    
    // 3.宣告時不設定大小,賦值後語言本身會計算陣列大小
    // var <陣列名稱> [<陣列長度>]<陣列元素> = [...]<元素型別>{元素1,元素2,...}
    var arr1 = [...]int{1, 2}
    arr2 := [...]int{1, 2, 3}
    fmt.Println(arr1) // 輸出:[1 2]
    fmt.Println(arr2) // 輸出:[1 2 3]
    //arr1[2] = 3 // 編譯報錯,陣列大小已設定為2
    
    // 4.宣告時不設定大小,賦值時指定索引
    // var <陣列名稱> [<陣列長度>]<陣列元素> = [...]<元素型別>{索引1:元素1,索引2:元素2,...}
    var arr3 = [...]int{1: 22, 0: 11, 2: 33}
    arr4 := [...]string{2: "cc", 1: "bb", 0: "aa"}
    fmt.Println(arr3) // 輸出:[11 22 33]
    fmt.Println(arr4) // 輸出:[aa bb cc]
    
    // 遍歷陣列
    for i := 0; i < len(arr4); i++ {
        v := arr4[i]
        fmt.Printf("i:%d, value:%sn", i, v)
    }
}

五、切片(slice)

因為陣列的長度定義後不可修改,所以需要切片來處理可變長陣列資料。切片可以看作是一個可變長的陣列,是一個參照型別。

它包含三個資料:

  • 指向原生陣列的指標
  • 切片中的元素個數
  • 切片已分配的儲存空間大小

注:瞭解c++和java的同學,可以參考vector和List,切片就是類似這兩個資料結構,直接上程式碼:

package main

import "fmt"

func main() {
    var sl []int             // 宣告一個切片
    sl = append(sl, 1, 2, 3) // 往切片中追加值
    fmt.Println(sl)          // 輸出:[1 2 3]

    var arr = [5]int{1, 2, 3, 4, 5} // 初始化一個陣列
    var sl1 = arr[0:2]              // 冒號:左邊為起始位(包含起始位資料),右邊為結束位(不包含結束位資料);不填則預設為頭或尾
    var sl2 = arr[3:]
    var sl3 = arr[:5]

    fmt.Println(sl1) // 輸出:[1 2]
    fmt.Println(sl2) // 輸出:[4 5]
    fmt.Println(sl3) // 輸出:[1 2 3 4 5]

    sl1 = append(sl1, 11, 22) // 追加元素
    fmt.Println(sl1)          // 輸出:[1 2 11 22]
}

使用make關鍵字直接建立切片,

語法:make([]型別, 大小,預留空間大小),make() 函數用於宣告slice切片、map字典、channel通道。

如下:

package main

import "fmt"

func main() {
    var s1 = make([]int, 5)          // 定義元素個數為5的切片
    s2 := make([]int, 5, 10)         // 定義元素個數5的切片,並預留10個元素的儲存空間(預留空間不知道有什麼用?)
    s3 := []string{`aa`, `bb`, `cc`} // 直接建立並初始化包含3個元素的陣列切片
    
    fmt.Println(s1, len(s1)) // 輸出:[0 0 0 0 0] 5
    fmt.Println(s2, len(s2)) // 輸出:[0 0 0 0 0] 5
    fmt.Println(s3, len(s3)) // [aa bb cc] 3
    
    s1[1] = 1 // 宣告或初始化大小中的資料,可以指定賦值
    s1[4] = 4
    //s1[5] = 5 // 編譯報錯,超出定義大小
    s1 = append(s1, 5)       // 可以追加元素
    fmt.Println(s1, len(s1)) // 輸出:[0 1 0 0 4 5] 6
    
    s2[1] = 1
    s2 = append(s2, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11)
    fmt.Println(s2, len(s2)) // 輸出:[0 1 0 0 0 1 2 3 4 5 6 7 8 9 10 11] 16
    
    // 遍歷切片
    for i := 0; i < len(s2); i++ {
        v := s2[i]
        fmt.Printf("i: %d, value:%d n", i, v)
    }
}

六、字典/對映(map)

map是一種鍵值對的無序集合,與 slice 類似也是一個參照型別。map 本身其實是個指標,指向記憶體中的某個空間。

宣告方式與陣列類似,宣告方式:var 變數名 map[key型別值型別 或直接使用 make 函數初始化:make(map[key型別]值型別, 初始空間大小)。

其中key值可以是任何可以用==判斷的值型別,對應的值型別沒有要求。

直接上程式碼,如下:

package main

import (
    "fmt"
    "unsafe"
)

func main() {
    // 宣告後賦值
    var m map[int]string
    fmt.Println(m) // 輸出空的map:map[]
    //m[1] = `aa`    // 向未初始化的map中賦值報錯:panic: assignment to entry in nil map

    // 宣告並初始化,初始化使用{} 或 make 函數(建立型別並分配空間)
    var m1 = map[string]int{}
    var m2 = make(map[string]int)
    m1[`a`] = 11
    m2[`b`] = 22
    fmt.Println(m1) // 輸出:map[a:11]
    fmt.Println(m2) // 輸出:map[b:22]

    // 初始化多個值
    var m3 = map[string]string{"a": "aaa", "b": "bbb"}
    m3["c"] = "ccc"
    fmt.Println(m3) // 輸出:map[a:aaa b:bbb c:ccc]

    // 刪除 map 中的值
    delete(m3, "a") // 刪除鍵 a 對應的值
    fmt.Println(m3) // 輸出:map[b:bbb c:ccc]

    // 查詢 map 中的元素
    v, ok := m3["b"]
    if ok {
        fmt.Println(ok)
        fmt.Println("m3中b的值為:", v) // 輸出:m3中b的值為: bbb
    }
    // 或者
    if v, ok := m3["b"]; ok { // 流程處理後面講
        fmt.Println("m3中b的值為:", v) // 輸出:m3中b的值為: bbb
    }

    fmt.Println(m3["c"]) // 直接取值,輸出:ccc

    // map 中的值可以是任意型別
    m4 := make(map[string][5]int)
    m4["a"] = [5]int{1, 2, 3, 4, 5}
    m4["b"] = [5]int{11, 22, 33}
    fmt.Println(m4)                // 輸出:map[a:[1 2 3 4 5] b:[11 22 33 0 0]]
    fmt.Println(unsafe.Sizeof(m4)) // 輸出:8,為8個位元組,map其實是個指標,指向某個記憶體空間
}

七、通道(channel)

說到通道 channel,則必須先了解下 Go 語言的 goroutine 協程(輕量級執行緒)。channel就是為 goroutine 間通訊提供通道。goroutine是 Go 語言提供的語言級的協程,是對 CPU 執行緒和排程器的一套封裝。

channel 也是型別相關的,一個 channel 只能傳遞一種型別的值。

宣告:var 通道名 chan 通道傳遞值型別 或 make 函數初始化:make(chan 值型別, 初始儲存空間大小)

說白了,通道類似訊息佇列,主要應用在並行程式設計裡面比較多,

直接上程式碼,如下:

package main

import (
    "fmt"
    "time"
)

func main() {
    var ch1 chan int            // 宣告一個通道
    ch1 = make(chan int)        // 未初始化的通道不能儲存資料,初始化一個通道
    ch2 := make(chan string, 2) // 宣告並初始化一個帶緩衝空間的通道

    // 通過匿名函數向通道中寫入資料,通過 <- 方式寫入
    go func() { ch1 <- 1 }()
    go func() { ch2 <- `a` }()

    v1 := <-ch1 // 從通道中讀取資料
    v2 := <-ch2
    fmt.Println(v1) // 輸出:1
    fmt.Println(v2) // 輸出:a

    // 寫入,讀取通道資料
    ch3 := make(chan int, 1) // 初始化一個帶緩衝空間的通道
    go readFromChannel(ch3)
    go writeToChannel(ch3)

    // 主執行緒休眠1秒,讓出執行許可權給子 Go 程,即通過 go 開啟的 goroutine,不然主程式會直接結束
    time.Sleep(1 * time.Second)
}

func writeToChannel(ch chan int) {
    for i := 1; i < 10; i++ {
        fmt.Println("寫入:", i)
        ch <- i
    }
}

func readFromChannel(ch chan int) {
    for i := 1; i < 10; i++ {
        v := <-ch
        fmt.Println("讀取:", v)
    }
}

執行結果如下:

// ------  輸出:--------
1
a
寫入: 1
寫入: 2
寫入: 3
讀取: 1
讀取: 2
讀取: 3
寫入: 4
寫入: 5
寫入: 6
讀取: 4
讀取: 5
讀取: 6
寫入: 7
寫入: 8
寫入: 9
讀取: 7
讀取: 8
讀取: 9

goroutine channel 的詳細用法會有相應的部落格專門來講這一章節,具體可在我的個人主頁裡面,找一下相關的部落格參考。

八、結構體(struct)

結構體是一種聚合的資料型別,是由零個或多個任意型別的值聚合成的實體。每個值稱為結構體的成員,和java中的class是一個意思:

package main

import "fmt"

// 定義一個結構體 person
type person struct {
    name string
    age  int
}

func main() {
    var p person   // 宣告一個 person 型別變數 p
    p.name = "max" // 賦值
    p.age = 12
    fmt.Println(p) // 輸出:{max 12}

    p1 := person{name: "mike", age: 10} // 直接初始化一個 person
    fmt.Println(p1.name)                // 輸出:mike

    p2 := new(person) // new函數分配一個指標,指向 person 型別資料
    p2.name = `張三`
    p2.age = 15
    fmt.Println(*p2) // 輸出:{張三 15}
}

九、介面(interface)

介面用來定義行為。Go 語言不同於物件導向語言,沒有類的概念,也沒有傳統意義上的繼承。Go 語言中的介面,用來定義一個或一組行為,某些物件實現了介面定義的行為,則稱這些物件實現了(implement)該介面,型別即為該介面型別。

定義介面也是使用 type 關鍵字,格式為:

// 定義一個介面
type InterfaceName interface {
    FuncName1(paramList) returnType
    FuncName2(paramList) returnType
    ...
}

實列:

package main

import (
    "fmt"
    "strconv"
)

// 定義一個 Person 介面
type Person interface {
    Say(s string) string
    Walk(s string) string
}

// 定義一個 Man 結構體
type Man struct {
    Name string
    Age  int
}

// Man 實現 Say 方法
func (m Man) Say(s string) string {
    return s + ", my name is " + m.Name
}

// Man 實現 Walk 方法,strconv.Itoa() 數位轉字串
func (m Man) Walk(s string) string {
    return "Age: " + strconv.Itoa(m.Age) + " and " + s
}

func main() {
    var m Man       // 宣告一個型別為 Man 的變數
    m.Name = "Mike" // 賦值
    m.Age = 30
    fmt.Println(m.Say("hello"))    // 輸出:hello, my name is Mike
    fmt.Println(m.Walk("go work")) // 輸出:Age: 30 and go work

    jack := Man{Name: "jack", Age: 25} // 初始化一個 Man 型別資料
    fmt.Println(jack.Age)
    fmt.Println(jack.Say("hi")) // 輸出:hi, my name is jack
}

十、錯誤(error)

error 型別本身是 Go 語言內部定義好的一個介面,介面裡定義了一個 Error() 列印錯誤資訊的方法,原始碼如下:

type error interface {
    Error() string
}

自定義錯誤資訊:

package main

import (
    "errors"
    "fmt"
)

func main() {
    // 使用 errors 客製化錯誤資訊
    var e error
    e = errors.New("This is a test error")
    fmt.Println(e.Error()) // 輸出:This is a test error

    // 使用 fmt.Errorf() 客製化錯誤資訊
    err := fmt.Errorf("This is another error")
    fmt.Println(err) // 輸出:This is another test error
}

到此這篇關於Go語言資料型別詳細介紹的文章就介紹到這了,更多相關Go資料型別內容請搜尋it145.com以前的文章或繼續瀏覽下面的相關文章希望大家以後多多支援it145.com!


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