天天看點

go語言資料類型

資料類型的出現是為了把資料分成所需記憶體大小不同的資料,程式設計的時候需要用大資料的時候才需要申請大記憶體,就可以充分利用記憶體。

Go 語言按類别有以下幾種資料類型:

類型 描述
布爾型 布爾型的值隻可以是常量 true 或者 false。一個簡單的例子:var b bool = true。
數字類型 整型 int 和浮點型 float,Go 語言支援整型和浮點型數字,并且原生支援複數,其中位的運算采用補碼。
字元串類型 字元串就是一串固定長度的字元連接配接起來的字元序列。Go的字元串是由單個位元組連接配接起來的。Go語言的字元串的位元組使用UTF-8編碼辨別Unicode文本。
派生類型 包括:(a) 指針類型(Pointer) (b) 數組類型 © 結構體類型(struct) (d) 聯合體類型 (union) (e) 函數類型 (f) 切片類型 (g) 接口類型(interface) (h) Map 類型 (i) Channel 類型

一、go 語言布爾類型

一個布爾類型的值隻有兩種:

true

false

if

for

語句的條件部分都是布爾類型的值,并且

==

<

等比較操作也會産生布爾型的值。

一進制操作符

!

對應邏輯非操作,是以!true的值為 false,更複雜一些的寫法是

(!true==false) ==true

,實際開發中我們應盡量采用比較簡潔的布爾表達式,就像用 x 來表示

x==true

package main

import "fmt"

func main() {
    var num = 10
    if num == 5 {
        fmt.Println("num == 5")
    } else {
        fmt.Println(num)
    }
    if num == 10 {
        fmt.Println("num == 10")
    } else {
        fmt.Println(num)
    }
    if num != 5 {
        fmt.Println("num != 5")
    } else {
        fmt.Println(num)
    }
    if num != 10 {
        fmt.Println("num != 10")
    } else {
        fmt.Println(num)
    }
}
// 結果如下
10
num == 10
num != 5
10
           

Go語言對于值之間的比較有非常嚴格的限制,隻有兩個相同類型的值才可以進行比較,如果值的類型是接口(interface),那麼它們也必須都實作了相同的接口。如果其中一個值是常量,那麼另外一個值可以不是常量,但是類型必須和該常量類型相同。如果以上條件都不滿足,則必須将其中一個值的類型轉換為和另外一個值的類型相同之後才可以進行比較。

布爾值可以和

&&(AND)

||(OR)

操作符結合,并且有短路行為,如果運算符左邊的值已經可以确定整個布爾表達式的值,那麼運算符右邊的值将不再被求值,是以下面的表達式總是安全的:

短路行為:當有多個表達式(值)時,左邊的表達式值可以确定結果時,就不再繼續運算右邊的表達式的值。

操作符 短路描述
&& 當左邊為True,還需要運算右邊,結果由右邊決定;當左邊為Flase,不需要右邊進行運算,結果為Flase
|| 當左邊為True,不需要運算右邊,結果為True;當左邊為Flase,需要計算右邊,結果由右邊決定
package main

import "fmt"


func main() {
    var s1 string = "nihao"
    // if s1 == "nihao" && s1[0] == 'n' {   //s1 符合要求
    // if s1 == "nihao" && s1[0] == 'x' {   //s1 不符合要求
    // if s1 == "nihao" && s1[0] == "n" {   // invalid operation: s1[0] == "n" (mismatched types byte and untyped string)
    // if s1 != "" && s1[0] == 'x' {     // s1 不符合要求
    // if s1 != "" && s1[0] == 'n' {        // s1 符合要求
    // if s1 == "nihao" || s1[0] == 'n' {     //s1 符合要求
    // if s1 == "nihao" || s1[0] == 'x' {       // s1 符合要求
    // if s1 != "" || s1[0] == 'x' {        // s1 符合要求
    if s1 != "" || s1[0] == 'n' {           // s1 符合要求
        fmt.Println("s1 符合要求")
    } else {
        fmt.Println("s1 不符合要求")
    }
}
           

其中 s[0] 操作如果應用于空字元串将會導緻 panic 異常(終端程式的異常)。

因為&&的優先級比||高(&& 對應邏輯乘法,|| 對應邏輯加法,乘法比加法優先級要高),是以下面的布爾表達式可以不加小括号:

if 'a' <= c && c <= 'z' ||
    'A' <= c && c <= 'Z' ||
    '0' <= c && c <= '9' {
    // ...ASCII字母或數字...
}
           

布爾值并不會隐式轉換為數字值 0 或 1,反之亦然,必須使用 if 語句顯式的進行轉換:

i := 0
if b {
    i = 1
}
           

如果需要經常做類似的轉換,可以将轉換的代碼封裝成一個函數,如下所示:

// 如果b為真,btoi傳回1;如果為假,btoi傳回0
func btoi(b bool) int {
    if b {
        return 1
    }
    return 0
}
           

數字到布爾型的逆轉換非常簡單,不過為了保持對稱,我們也可以封裝一個函數:

// itob報告是否為非零。
func itob(i int) bool { return i != 0 }
           

Go語言中不允許将整型強制轉換為布爾型,代碼如下:

var n bool
fmt.Println(int(n) * 2)
           

編譯錯誤,輸出如下:

布爾型無法參與數值運算,也無法與其他類型進行轉換。

二、go 語言數字類型

Go語言的數值類型分為以下幾種:整數、浮點數、複數,其中每一種都包含了不同大小的數值類型,例如有符号整數包含 int8、int16、int32、int64 等,每種數值類型都決定了對應的大小範圍和是否支援正負符号。

2.1、Go語言整型(整數類型)

Go語言同時提供了有符号和無符号的整數類型,其中包括

int8、int16、int32 和 int64

四種大小截然不同的有符号整數類型,分别對應

8、16、32、64

bit(二進制位)大小的有符号整數,與此對應的是

uint8、uint16、uint32 和 uint64

四種無符号整數類型。

此外還有兩種整數類型 int 和 uint,它們分别對應特定 CPU 平台的字長(機器字大小),其中 int 表示有符号整數,應用最為廣泛,uint 表示無符号整數。實際開發中由于編譯器和計算機硬體的不同,int 和 uint 所能表示的整數大小會在 32bit 或 64bit 之間變化。

大多數情況下,我們隻需要 int 一種整型即可,它可以用于循環計數器(for 循環中控制循環次數的變量)、數組和切片的索引,以及任何通用目的的整型運算符,通常 int 類型的處理速度也是最快的。

用來表示 Unicode 字元的

rune

類型和 int32 類型是等價的,通常用于表示一個 Unicode 碼點。這兩個名稱可以互換使用。同樣,byte 和 uint8 也是等價類型,byte 類型一般用于強調數值是一個原始的資料而不是一個小的整數。

最後,還有一種無符号的整數類型

uintptr

,它沒有指定具體的 bit 大小但是足以容納指針。uintptr 類型隻有在底層程式設計時才需要,特别是Go語言和C語言函數庫或作業系統接口互相動的地方。

盡管在某些特定的運作環境下 int、uint 和 uintptr 的大小可能相等,但是它們依然是不同的類型,比如 int 和 int32,雖然 int 類型的大小也可能是 32 bit,但是在需要把 int 類型當做 int32 類型使用的時候必須顯示的對類型進行轉換,反之亦然。

Go語言中有符号整數采用 2 的補碼形式表示,也就是最高 bit 位用來表示符号位,一個 n-bit 的有符号數的取值範圍是從

-2(n-1) 到 2(n-1)-1

。無符号整數的所有 bit 位都用于表示非負數,取值範圍是

0 到 2n-1

。例如,int8 類型整數的取值範圍是從

-128 到 127

,而 uint8 類型整數的取值範圍是從

0 到 255

哪些情況下使用 int 和 uint

程式邏輯對整型範圍沒有特殊需求。例如,對象的長度使用内建

len()

函數傳回,這個長度可以根據不同平台的位元組長度進行變化。實際使用中,切片或 map 的元素數量等都可以用 int 來表示。

反之,在二進制傳輸、讀寫檔案的結構描述時,為了保持檔案的結構不會受到不同編譯目标平台位元組長度的影響,不要使用 int 和 uint。

2.2、Go語言浮點類型(小數類型)

Go語言提供了兩種精度的浮點數

float32

float64

,它們的算術規範由

IEEE754

浮點數國際标準定義,該浮點數規範被所有現代的 CPU 支援。

這些浮點數類型的取值範圍可以從很微小到很巨大。浮點數取值範圍的極限值可以在 math 包中找到:

  • 常量

    math.MaxFloat32

    表示

    float32

    能取到的最大數值,大約是

    3.4e38

  • 常量

    math.MaxFloat64

    表示

    float64

    能取到的最大數值,大約是

    1.8e308

  • float32

    float64

    能表示的最小值分别為

    1.4e-45

    4.9e-324

一個 float32 類型的浮點數可以提供大約 6 個十進制數的精度,而 float64 則可以提供約 15 個十進制數的精度,通常應該優先使用 float64 類型,因為 float32 類型的累計計算誤差很容易擴散,并且 float32 能精确表示的正整數并不是很大。如下:

package main

import "fmt"

func main() {
    var f float32 = 16777216 // 1 << 24
    var f1 float64 = 16777216
    fmt.Println(f)                // 1.6777216e+07
    fmt.Println(f1)               // 1.6777216e+07
    fmt.Println(f +1)             // 1.6777216e+07
    fmt.Println(f == f+1)         // true
    fmt.Println(f1 + 1)           // 1.6777217e+07
    fmt.Println(f1 == f1 + 1)     // false
}
           

浮點數在聲明的時候可以隻寫整數部分或者小數部分,像下面這樣:

const e = .71828 
const f = 1.     
fmt.Println(e)   // 0.71828
fmt.Println(f)   // 1
           

很小或很大的數最好用科學計數法書寫,通過 e 或 E 來指定指數部分:

const Avogadro = 6.02214129e23  // 阿伏伽德羅常數
const Planck   = 6.62606957e-34 // 普朗克常數
           

用 Printf 函數列印浮點數時可以使用

%f

來控制保留幾位小數

package main

import (
    "fmt"
    "math"
)

func main() {
    fmt.Printf("%f\n", math.Pi)     // 3.141593
    fmt.Printf("%.2f\n", math.Pi)   // 3.14
}
           

2.3、Go語言複數

在計算機中,複數是由兩個浮點數表示的,其中一個表示實部(real),一個表示虛部(imag)。

Go語言中複數的類型有兩種,分别是 complex128(64 位實數和虛數)和 complex64(32 位實數和虛數),其中 complex128 為複數的預設類型。

複數的值由三部分組成

RE + IMi

,其中 RE 是實數部分,IM 是虛數部分,RE 和 IM 均為 float 類型,而最後的 i 是虛數機關。

聲明複數的文法格式如下所示:

其中 name 為複數的變量名,complex128 為複數的類型,"="後面的 complex 為Go語言的内置函數用于為複數指派,x、y 分别表示構成該複數的兩個 float64 類型的數值,x 為實部,y 為虛部。

上面的聲明語句也可以簡寫為下面的形式:

對于一個複數

z := complex(x, y)

,可以通過Go語言的内置函數

real(z)

來獲得該複數的實部,也就是 x;通過

imag(z)

獲得該複數的虛部,也就是 y。

使用内置的 complex 函數建構複數,并使用 real 和 imag 函數傳回複數的實部和虛部:

var x complex128 = complex(1, 2) // 1+2i
var y complex128 = complex(3, 4) // 3+4i
fmt.Println(x*y)                 // "(-5+10i)"
fmt.Println(real(x*y))           // "-5"
fmt.Println(imag(x*y))           // "10"
           

複數也可以用==和!=進行相等比較,隻有兩個複數的實部和虛部都相等的時候它們才是相等的。

Go語言内置的

math/cmplx

包中提供了很多操作複數的公共方法,實際操作中建議大家使用複數預設的 complex128 類型,因為這些内置的包中都使用 complex128 類型作為參數。

複數運算法則:https://baike.baidu.com/item/%E5%A4%8D%E6%95%B0%E8%BF%90%E7%AE%97%E6%B3%95%E5%88%99/2568041?fr=aladdin

三、Go語言字元串

一個字元串是一個不可改變的位元組序列,字元串可以包含任意的資料,但是通常是用來包含可讀的文本,字元串是 UTF-8 字元的一個序列(當字元為 ASCII 碼表上的字元時則占用 1 個位元組,其它字元根據需要占用 2-4 個位元組)。

UTF-8 是一種被廣泛使用的編碼格式,是文本檔案的标準編碼,其中包括 XML 和 JSON 在内也都使用該編碼。由于該編碼對占用位元組長度的不定性,在Go語言中字元串也可能根據需要占用 1 至 4 個位元組,這與其它程式設計語言如 C++、Java 或者 Python 不同(Java 始終使用 2 個位元組)。Go語言這樣做不僅減少了記憶體和硬碟空間占用,同時也不用像其它語言那樣需要對使用 UTF-8 字元集的文本進行編碼和解碼。

字元串是一種值類型,且值不可變,即建立某個文本後将無法再次修改這個文本的内容,更深入地講,字元串是位元組的定長數組。

3.1、定義字元串

可以使用雙引号

""

來定義字元串,字元串中可以使用轉義字元來實作換行、縮進等效果,常用的轉義字元包括:

  • \n:換行符
  • \r:回車符
  • \t:tab 鍵
  • \u 或 \U:Unicode 字元
  • \:反斜杠自身

如下:

package main

import "fmt"

func main() {
    var s string = "hello Jack!"
    var s1 string = "hello \nJack!"
    var s2 string = "hello \rJack!"
    var s3 string = "hello \tJack!"
    var s4 string = "hello \\Jack!"
    fmt.Println(s)
    fmt.Println(s1)
    fmt.Println(s2)
    fmt.Println(s3)
    fmt.Println(s4)
}

// 結果如下:
hello Jack!
hello 
Jack!
Jack! 
hello   Jack!
hello \Jack!
           

一般的比較運算符(

==、!=、<、<=、>=、>

)是通過在記憶體中按位元組比較來實作字元串比較的,是以比較的結果是字元串自然編碼的順序。字元串所占的位元組長度可以通過函數 len() 來擷取,例如 len(str)。

var s string = "hello Jack!"
var s1 string = "hello \nJack!"
fmt.Println(len(s))   // 11
fmt.Println(len(s1))  // 12
           

字元串的内容(純位元組)可以通過标準索引法來擷取,在方括号

[ ]

内寫入索引,索引從 0 開始計數:

  • 字元串 str 的第 1 個位元組:str[0]
  • 第 i 個位元組:str[i - 1]
  • 最後 1 個位元組:str[len(str)-1]

需要注意的是,這種轉換方案隻對純 ASCII 碼的字元串有效。

注意:擷取字元串中某個位元組的位址屬于非法行為,例如 &str[i]。

var s string = "hello Jack!"
fmt.Println(s[3])   //108
fmt.Println(s[4])   //111
           

3.2、字元串拼接符

+

兩個字元串 s1 和 s2 可以通過

s := s1 + s2

拼接在一起。将 s2 追加到 s1 尾部并生成一個新的字元串 s。

可以通過下面的方式來對代碼中多行的字元串進行拼接:

str := "Beginning of the string " +
"second part of the string"
fmt.Println(str01)
// 因為編譯器會在行尾自動補全分号,是以拼接字元串用的加号“+”必須放在第一行末尾
           

也可以使用

+=

來對字元串進行拼接:

str02 := "hel" + "lo"
fmt.Println(str02)
str02 += " world"
fmt.Println(str02)
           

3.3、定義多行字元串

在Go語言中,使用雙引号書寫字元串的方式是字元串常見表達方式之一,被稱為字元串字面量(string literal),這種雙引号字面量不能跨行,如果想要在源碼中嵌入一個多行字元串時,就必須使用`反引号,代碼如下:

str_more := `first line
second line
    `
fmt.Println(str_more)

first line
second line
           

在這種方式下,反引号間換行将被作為字元串中的換行,但是所有的轉義字元均無效,文本将會原樣輸出。

在` 間的所有代碼均不會被編譯器識别,而隻是作為字元串的一部分。