Go基礎系列:簡單數據類型

每個變量都有數據類型,Go中的數據類型有:python

  • 簡單數據類型:int、float、complex、bool和string
  • 數據結構或組合(composite):struct、array、slice、map和channel
  • 接口(interface)

當聲明變量的時候,會作默認的賦0初始化。每種數據類型的默認賦0初始化的0值不一樣,例如int類型的0值爲數值0,float的0值爲0.0,string類型的0值爲空"",bool類型的0值爲false,數據結構的0值爲nil,struct的0值爲字段所有賦0。數組

其實函數也有類型,不過通常稱之爲返回類型。例如,下面的函數foo的返回類型是int:數據結構

func foo() int {
    ...CODE...
    return INT_TYPE_VALUE
}

函數容許有多個返回值,它們使用逗號分隔,括號包圍:架構

func foo() (int,bool)

Number類型

Integer

Integer類型是整型數據,例如3 22 0 1 -3 -22等。app

Go中的Integer有如下幾種細分的類型:函數

  • int8,int16,int32,int64
  • uint8,uint16,uint32,uint64
  • byte
  • rune
  • int,uint

其中8 16 32 64表示該數據類型能存儲的bit位數。例如int8表示能存儲8位數值,因此這個類型佔用1字節,也表示最大能存儲的整型數共2^8=256個,因此int8類型容許的最大正數爲127,容許的最小負數爲-128,共256個數值。學習

uint中的u表示unsigned,即無符號整數,只保存0和正數。因此uint8能存儲256個數的時候,容許的最小值爲0,容許的最大值爲255。ui

額外的兩種Integer是byte和rune,它們分別等價於uint8(即一個字節大小的正數)、int32。從builtin包中的定義就能夠知道:指針

$ go doc builtin | grep -E "byte|rune"
type byte = uint8
type rune = int32

byte類型後面會詳細解釋。code

還有兩種依賴於CPU位數的類型int和uint,它們分別表示一個機器字長。在32位CPU上,一個機器字長爲32bit,共4字節,在64位CPU上,一個機器字長爲64bit,共8字節。除了int和uint依賴於CPU架構,還有一種uintptr也是依賴於機器字長的。

通常來講,須要使用整型數據的時候,指定int便可,有明確的額外需求時再考慮是否換成其它整數類型。

在整數加上0前綴表示這是8進制,例如077。加上前綴0x表示這是16進制,例如0x0c,使用e符號能夠表示這是一個科學計數法,如1e3 = 1000,6.023e23 = 6.023 x 10^23

可使用TYPE(N)的方式來生成一個數值,例如a := uint64(5)。實際上這是類型轉換,將int類型的5轉換成int64類型的5。

byte類型

Go中沒有專門提供字符類型char,Go內部的全部字符類型(不管是ASCII字符仍是其它多字節字符)都使用整數值保存,因此字符能夠存放到byte、int等數據類型變量中。byte類型等價於uint8類型,表示無符號的1字節整數。

Go中的字符都使用單引號包圍,例如'a''我',但單引號中包含了多個字符是錯誤的(如'aA'),由於字符類型就是一個字符。

例如,ASCII的字母a表示97。下面這種定義方式是容許的:

var a byte = 'A'  // a=65
var b uint8 = 'a' // b=97

注意,字符必須使用單引號,且必須只能是單個字符。因此byte類型常常被稱爲character類型。

如下也都是容許的:

var a = 'A'
var a uint32 = 'A'
var a int64 = 'A'

因此,Integer類型當存儲的是以單引號包圍的字符時,它會將字符轉換成它二進制值對應的數值。一樣適用於unicode字符,它將用來存放各字節對應的二進制的數值:

var a int64 = '我'  // a=25105

因爲在Go中佔用3字節,因此保存到byte中是報錯的:

var a byte = '我'

能夠保存它的unicode字符的代碼點:

var a byte = '\u0041'  // a=65,表明的字符A

若是不知道代碼點的值,能夠將其以int類型保存並輸出。

fmt.Printf("%d", '我')  // 25105

若是想將byte值轉換爲字符,可使用string()函數作簡單的類型轉換:

var a = 'A'
println(string(a))     // 輸出:A

float和complex

float是浮點數(俗稱小數),例如0.0 3.0 -3.12 -3.120等。

Go中的浮點數類型float有兩種:float32和float64。

complex表示複數類型(虛數),有complex64和complex128。

浮點數在計算機系統中很是複雜,對於學習來講,只需將其認爲是數學中的一種小數便可。但如下幾個注意點須要謹記心中:

  1. 浮點數是不精確的。例如1.01-0.99從數學運算的角度上獲得的值是0.02,但實際上的結果是0.020000000000000018(python運算的結果),在Go中會將其表示爲+2.000000e-002。這個結果是一種極限趨近於咱們期待值的結果。
  2. float32的精度(7個小數位)低於float64(15個小數位),因此float64類型的值比float32類型的值更精確。
  3. 由於浮點數不精確,因此儘可能不要對兩個浮點數數進行等值==和不等值!=比較,例如(3.2-2.8) == 0.4返回Flase。若是非要比較,應該經過它們的減法求絕對值,再與一個足夠小(不會影響結果)的值作不等比較,例如abs((3.2-2.8)-0.4) < 0.0002返回True。

通常來講,在程序中須要使用浮點數的時候都使用float64類型,不只由於精確,更由於幾乎全部包中須要float參數的類型都是float64。

在Go的數學運算中,默認取的是整型數據,若是想要獲得浮點數結果,必須至少讓運算的一方寫成浮點數格式:

var a := 3/2     // a獲得截斷的整數:a=1
var b := 3/2.0   // b爲浮點數b=+1.500000e+000
var c := 3 + 2.0 // c爲浮點數

string類型

Go中的string用於保存UTF-8字符序列,它是動態大小的。對於字母和英文字母,它佔用一個字節,對於其它unicode字符,按需佔用2-4個字節。例如中文字符佔用3個字節。

Go中的string類型要使用雙引號或反引號包圍,它們的區別是:

  • 雙引號是弱引用,其內可使用反斜線轉義符號,如ab\ncd表示ab後換行加cd
  • 反引號是強引用,其內任何符號都被強制解釋爲字面意義,包括字面的換行。也就是所謂的裸字符串。
func main() {
    println("abc\ndef")
    println(`ABC
    DEF`)
}

上面的結果將輸出:

abc
def
ABC
    DEF

不能使用單引號包圍,單引號包圍的表示它的二進制值轉換成十進制的數值。例如字母對應的是ASCII碼。這個在前面byte類型中介紹過。因此,使用單引號包圍的字符其實是整數數值。例如'a'等價於97。

string的底層是byte數組,每一個string其實只佔用兩個機器字長:一個指針和一個長度。只不過這個指針在Go中徹底不可見,因此對咱們來講,string是一個底層byte數組的值類型而非指針類型。

因此,能夠將一個string使用append()或copy()拷貝到一個給定的byte slice中,也可使用slice的切片功能截取string中的片斷。

func main() {
    var a = "Hello Gaoxiaofang"
    println(a[2:3])      // 輸出:l

    s1 := make([]byte,30)
    copy(s1,a)          // 將字符串保存到slice中
    println(string(s1)) // 輸出"Hello Gaoxiaofang"
}

字符串串接

使用加號+鏈接兩段字符串:"Hello" + "World"等價於"HelloWorld"。

能夠經過+的方式將多行鏈接起來。例如:

str := "Beginning string "+
       "second string"

字符串鏈接+操做符強制認爲它兩邊的都是string類型,因此"abcd" + 2將報錯。須要先將int類型的2轉換爲字符串類型(不能使用string(2)的方式轉換,由於這種轉換方式不能跨大類型轉換,只能使用strconv包中的函數轉換)。

另外一種更高效的字符串串接方式是使用strings包中的Join()函數,它能夠在緩衝中將字符串串接起來。

字符串長度

使用len()取字節數量(不是字符數量)。

例如len("abcde")返回5,size(我是中國人)返回15。

字符串截取

能夠將字符串看成數組,使用索引號取部分字符串(按字節計算),索引號從0開始計算,如"abcd"[1]

從字符串取字符的時候,須要注意的是index按字節計算而非按字符計算。兩種取數據方式:

"string"[x]
"string"[x:y]

第一種方式將返回第(x+1)個字節對應字符的二進制數值,例如字母將轉換爲ASCII碼,unicode將取對應字節的二進制轉換爲數值。

第二種方式將返回第(x+1)字節到第y字節中間的字符,Go中採起"左閉右開"的方式,因此所截取部分包括index=x,但不包括index=y。

例如:

func main() {
    println("abcde"[1])          // (1).輸出"98"
    println("我是中國人"[1])       // (2).輸出"136"
    println("abcde"[0:2])        // (3).輸出"ab"
    println("我是中國人"[0:3])     // (4).輸出"我"
    println("abcde"[3:4])        // (5).輸出"d"
}

分析每一行語句:

  • (1).取第2個字節的二進制值,即字符b對應的值,其ASCII爲98
  • (2).取第2個字節的二進制值,由於中文佔用3個字節,因此取第一個字符"我"的第二個字節部分,轉換爲二進制值,爲136
  • (3).取第1個字節到第3個字節(不包括)中間的字符,因此輸出"ab"
  • (4).取前三個字節對應的字符,因此輸出"我"
  • (5).取第4個字節對應的字符,因此輸出d

字符串遍歷

字符串是字符數組,若是字符串中全是ASCII字符,直接遍歷便可,但若是包含了多字節字符,則能夠[]rune(str)轉換後後再遍歷。

package main

import "fmt"

func main() {
    str := "Hello 你好"
    r := []rune(str)  // 8
    for i := 0; i < len(r); i++ {
        fmt.Printf("%c", r[i])
    }
}

字符串比較

可使用< <= > >= == !=對字符串進行比較,它將一個字符一個字符地比對。字母以A-Za-z的ASCII方式排列。

// 字符串比較
println("a" < "B")  // false

// 數值比較,不是字符串比較
println('a' == 97)  // true

修改字符串

字符串是一個不可變對象,因此對字符串s截取後賦值的方式s[1]="c"會報錯。

要想修改字符串中的字符,必須先將字符串拷貝到一個byte slice中,而後修改指定索引位置的字符,最後將byte slice轉換回string類型。

例如,將"gaoxiaofang"改成"maoxiaofang":

s := "gaoxiaofang"
bs := []byte(s)
bs[0] = 'm'     // 必須使用單引號
s = string(bs)
println(s)

注意修改字符的時候,必須使用單引號,由於它是byte類型。

布爾類型(bool)

bool類型的值只有兩種:true和false。

有3種布爾邏輯運算符:&& || !,分別別是邏輯與,邏輯或,取反。

func main() {
    println(true && true)    // true
    println(true && false)   // false
    println(true || true)    // true
    println(true || false)   // true
    println(!true)           // false
}

Go是一門很是嚴格的怨言,在使用==進行等值比較的時候,要求兩邊的數據類型必須相同,不然報錯。若是兩邊數據類型是接口類型,則它們必須實現相同的接口函數。若是是常量比較,則兩邊必須是可以兼容的數據類型。

在printf類的函數的格式中,佔位符%t用於表明布爾值。

布爾類型的變量、函數名應該以is或Is的方式開頭來代表這是一個布爾類型的東西。例如isSorted()函數用於檢測內容是否已經排序,IsFinished()用於判斷是否完成。

type關鍵字:類型別名

可使用type定義本身的數據類型,例如struct、interface。

還可使用type定義類型的別名。例如,定義一個int類型的別名INT:

type INT int

這樣INT類型的底層數據結構仍是int類型。能夠將它和int同樣使用:

var a INT = 5

type中能夠一次性聲明多個別名:

type (
    CT int
    IT int32
    DT float32
)

獲取數據類型

reflect包的TypeOf(),或者Printf/Sprintf的"%T"。

package main

import (
    "reflect"
    "fmt"
)

type IT int32
func main() {
    var a IT = 322
    var b = 22
    fmt.Println(reflect.TypeOf(a))   // main.IT
    fmt.Println(reflect.TypeOf(b))   // int
    fmt.Println(fmt.Sprintf("%T", a)) // main.IT
}

數據類型的大小

unsafe包的Sizeof()查看變量或常量所屬數據類型佔用空間的大小。

package main

import (
    "unsafe"
    "fmt"
)

type IT int32
func main() {
    var a IT = 322
    var b = 22
    fmt.Println(unsafe.Sizeof(a)) // 4
    fmt.Println(unsafe.Sizeof(b)) // 8
}
相關文章
相關標籤/搜索