golang筆記——數據類型

25個關鍵字

  程序聲明:import, package

  程序實體聲明和定義:chan, const, func, interface, map, struct, type, var

  程序流程控制:go, select, break, case, continue, default, defer, else, fallthrough, for, goto, if, range, return

 

類型

  18個基本類型:bool, string, rune, byte, int, uint, int8, uint8, int16, uint16, int32, uint32, int64, uint64, float32, float64, complex64, complex128

  7個複合類型:array, struct, function, interface, slice, map, channel

  其中,切片、字典、通道類型都是引用類型

  類型的聲明一般以 type 關鍵字開始,然後是自定義的標識符名稱,然後是基本類型的名稱或複合類型的定義。

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

  最後,還有一種無符號的整數類型uintptr,沒有指定具體的bit大小但是足以容納指針。uintptr類型只有在底層編程是才需要,特別是Go語言和C語言函數庫或操作系統接口相交互的地方。 

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

 

操作符

  列舉一些特殊的操作符,注意下面的位操作符

&      位運算 AND
|      位運算 OR
^      位運算 XOR
&^     位清空 (AND NOT)
<<     左移
>>     右移

  可以通過 Printf 函數的 %b 參數來輸出二進制格式的數字。

 

特殊的空標識符

  下劃線 _ 作爲一個特殊的標識符,可以用於 import 語句中,僅執行導入包中的 init 方法。也可以作爲賦值語句的左邊,表示該變量並不關心且不使用。

  此外,標識符首字母的大小寫,在GO語言中被用來控制變量或函數的訪問權限,類似於其它語言的 public\private。

 

類型斷言

  比較特殊的表達式有類型斷言,如果判斷一個表達式 element 的類型是 T 的話,表達式爲 element.(T),意思是 element 不爲 nil 且存儲在其中的值是T類型。這裏有兩種情況,如果 T 不是一個接口類型,則 element 必須要爲接口類型的值,比如判斷 100 是 int 型,不能使用 100.(int),而要用 interface{}(100).(int) ; 如果T 是一個接口類型,表示斷言 element 實現了 T 這個接口。如果函數的參數是一個空接口,則必須斷言傳入的接口實現類型,才能使用其對應的方法。

 

可變參函數

  最後一個參數爲 ...T 的形式的函數即爲可變參函數,意味着可變參數都是 T 類型(或實現了T的類型)如:func CallFunction(first string, t ...string),GO語言會在調用可變參函數時,創建一個切片,然後將這些可變參數放入切片中,但如果傳入的可變參部分就是一個元素類型爲T的切片的話,則直接把傳入切片賦值給創建的切片,且在調用寫法上也有區別,爲: CallFunction("hello", []string{"x","y"}...) 

 

數組類型

  array: 聲明一個長度爲 n 、元素類型爲 T 的數組爲: [n]T, 元素類型可以爲基本類型也可以爲複合類型,也可以不指定 n ,由推導得出,如: [...]string{"a","b"} , 數組長度 n = len([...]string{"a","b"}),另外如果指定了數組長度,但定義的數組長度小於聲明的長度,則以聲明長度爲準,不足的元素補默認值。同一元素類型,但數組長度不同,則視爲不同類型。

 

切片類型

  slice: 切片類型的聲明爲 []T數組,切片類型裏沒有關於長度的規定,其它跟數組一樣,切片類型的零值是 nil。切片總是對應於一個數組,其對應的數組稱爲底級數組。切片和其底層數組的關係是引用關係,如果有修改都會影響到對方。

  切片的數據結構包含了指向其底層數組的指針、切片長度和切片容量。切片的長度很容易理解,切片的容量是什麼呢,它是切片第一個元素到底層數組最後一個元素的長度。

 

字典類型

  map: 定義一個哈希表的格式爲 map[K]V,其中 K 表示鍵的類型,V表示值的類型,如: map[string]bool{"IsOK":true, "IsError":false}

 

接口類型

  定義了一組方法聲明,接口中可以包含接口

  GO語言對接口類型的實現是非侵入式的(備註:侵入式是指用戶代碼與框架代碼有依賴,而非侵入式則沒有依賴,或者說耦合),只要一個類型定義了某個接口中聲明的所有方法,就認爲它實現了該接口。

  一個特殊的接口: interface{} 是一個空接口,不包含任何方法聲明,所以GO語言所有的類型都可以看成是它的實現類型,我們就可以使用它實現類似其它語言中的公共基類的功能。比如聲明一個字典,鍵是字符串,值是不確定類型,就可以使用 map[string]interface{} 

  判斷一個類型是否實現了一個接口,可以通過類型斷言來確定: _, ok := interface{}(MyType{}).(MyInterface)

 

函數與方法

  GO語言中,函數跟方法是有區別的,函數就是我們通常理解的函數,而方法是附屬於某個自定義數據類型的函數,即存在某個接收者。

  func (self MyType) Len() int {}     這裏的 (self MyInterface) 是表示方法接收者。

  值方法和指針方法,值方法是指接收者是一個對象,而指針方法是指接收者是一個對象指針。兩者的區別是,值方法中對接收者的改變是副本級別的,而指針方法中對接收者的改變是地址級別的。所以其實一般都推薦使用指針方法,因爲大多數情況下我們在方法內部修改接收者,都是爲了真實的改變它,而不是改變一個副本。但是,對於引用類型的接收者來說,兩者並無區別。

  匿名函數由函數字面量表示,函數是作爲值存在的,可以賦給函數類型的變量。如:

    var myfunc func(int, int) int
    myfunc = func(x, y int) (result int) {
        result = x + y
        return
    }
    log.Println(myfunc(3, 4))

  一個方法的類型是一個函數類型,即把接收者放到函數的第一個參數位置即可。

  非常遺憾,GO語言不支持函數和方法重載。

 

結構體

  可以包含字段,也可以包含匿名字段,一般匿名字段是接口類型,這樣就相當於該結構體包含了該接口中的方法,另外也可以在結構裏重寫隱藏接口中的方法。

  

指針

  有一個專門用於存儲內存地址的類型 unitptr,它與 int/unit 一樣屬於數值類型,存儲32/64位無符號整數。

  可以在一個類型前面使用*號,來表示對應的指針類型,也可以在可尋址的變量前面使用&,來獲取其地址。

 

常量

  定義常量和多個常量如下:

複製代碼
    const PP = iota        //0
    const QQ = iota          //0

    const (
        A = 1
        B = 2
        C
        D = iota
        E
        F 
    )
    log.Print(A, B, C, D, E, F)
//輸出是: 1 2 2 3 4 5
複製代碼

  注意,iota 只能在 const 裏使用,它是 const 的行數索引器,其值是所在const組中的索引值,單常量定義時 iota 就等於 0。另外,const組中某一項如果不賦值,則默認和上一個值一樣,但如果前面是 iota ,則爲上一個值+1。使用 iota 可以實現其它語言中的枚舉。

 

變量

  變量的聲明語句以 var 開始,聲明多個變量時,和聲明多個const的方法相同。

var x string = "df"
var x = "df"
x := "df"        //此爲簡寫形式。

 

數據初始化

  GO語言的數據初始化有兩種方法,一是使用 new ,一是使用 make ,其中 new 是爲某個類型分配內存,並設置零值後返回地址,如 new(T) 返回的就是指向T類型值指針值,即*T。如 new([3]int) 其實相當於 [3]int{0,0,0},所以它是一種很乾淨的內存分配策略。

  make 只用於創建切片類型、字典類型和通道類型(注意這三個類型的特點,都是引用類型),它對這些類型創建之後,還會進行必要的初始化,與 new 不同,它返回的就是指T類型的值,而不是指針值。

定義常量的方式是使用 const ,如 const PI = 3.14,如果定義多個常量可以使用 

編譯器會自動選擇在棧上還是在堆上分配局部變量的存儲空間,但可能令人驚訝的是,這個選擇並不是由用var還是new聲明變量的方式決定的。 如果一個函數裏聲明一個局部變量,但是將其指針賦給一個全局變量,那麼則不能將此局部變量放在棧中,而只能放在堆中,我們可以稱之爲該局部變量逃逸了,所以關於變量是分配在棧上還是堆上,是由編譯器根據情況來選擇的。

 

內建函數

  close 只接受通道類型的值

  len函數,可以應用於字符串、切片、數組、字典、通道類型

  cap函數,可以應用於切片、數組、通道類型

  new函數和make函數

  append函數和copy函數,應用於切片

  delete函數,根據字典的鍵刪除某一項

  complex\real\imag函數,複數相關

  panic 函數,異常相關,它的參數一般是某個error接口的某個實現類型;recover 函數,不接受任何參數,返回 interface{} 類型,也就是意味着,它可以返回任意類型。recover返回的內容是與panic相關的,就是panic的參數內容。

  print\println 函數,這兩個函數支持基本類型參數,且是可變參數。但輸出格式是固定的,且GO語言不保證以後會保留這兩個函數,所以知道就好,不推薦使用。可以使用 fmt.Print 和 fmt.Println 來代替,效果更佳。

 

合併書寫:

和 var/const 類似,多個 type 定義可合併成組,如下:

複製代碼
type (
    Person struct {
        Name string
        Age  int32
    }   

    myfunc func(string) bool
)
複製代碼

 尤其是在函數內部定義一堆臨時類型時,可集中書寫,可讀性更好。

 

 

自增/自減運算符不同於其它語言,不能用作表達式,只能當獨立的語句使用,且不能前置,只有後置形式,以儘可能避免該運算符帶的複雜性問題。

 

 

unsafe.Pointer 與 uintptr 的區別:

前者類似 C 語言中的 void* 萬能指針,能安全持有對象,但後者不行,後者只是一種特殊整型,並不引用目標對象,無法阻止垃圾回收器回收對象內存。

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章