Golang 筆記 1 基礎、基本數據類型

一、Go語言基礎

1. 基礎

Go語言中的標識符必須以字母(Unicode字母,PHP/JS可以用中文作爲變量名)下劃線開頭。大寫字母跟小寫字母是不同的:Hello和hello是兩個不同的名字。
  Go中有25個關鍵字:

break    default     func   interface select
case     defer       go     map       struct
chan     else        goto   package   switch
const    fallthrough if     range     type
continue for         import return    var

  如果一個名字是在函數內容定義,那麼它的作用域就在函數內容,如果在函數外部定義,那麼將在當前包的所有文件中都可以訪問。名字的開頭字母的大小寫決定了名字在包外的可見性。如果一個名字是大寫字母開頭,那麼它可以被外部的包訪問,例如fmt包的Printf函數。

2. 註釋

  • 單行註釋 // ...
  • 多行註釋 /* ... */

3. Go程序的一般結構

  • Go程序是通過package來組織的
  • 只有package名稱爲main的包可以包含main函數
  • 一個可執行程序有且只有一個main包
// 當前程序的包名
package main
// 導入其他的包
import "fmt"
// 一次導入多個包
import (
    "io"
    "os"
)
// 常量的定義
const PI = 3.14
// 全局變量的聲明與賦值,該變量在整個package中使用
var name = "go"
// 一般類型聲明
type newType int
// 結構的聲明
type go struct{}
// 接口的聲明
type golang interface{}
// 由main函數作爲程序入口啓動
func main() {
    fmt.Println("Hello World!")
}
  • 如果導入的包但是沒有用到類型或者函數則會報編譯錯誤
  • package別名: import io "fmt" 這樣將fmt設置一個別名爲io,調用的時候就可以這樣: io.Println("...")

4. 可見性規則

  Go語言中使用大小寫來決定該常量、變量、類型、接口、結構或函數是否可以被外部包所調用: 根據約定,函數名首字母小寫即爲private(外部不可以調用),大寫爲public

二、基本數據類型

1、變量和常量

  • 普通賦值:
// var 變量名稱 變量類型 = 值
var num int = 1
  • 平行賦值
var num1,num2 int = 1, 2
  • 多行賦值
var (
    num1 int = 1
    num2 int = 2
)

2、整數類型的命名和寬度

  Go的整數類型一共有10個
  其中計算架構相關的整數類型有兩個: 有符號的整數類型 int, 無符號的整數類型 uint。
  在不同計算架構的計算機上,它們體現的寬度(存儲某個類型的值所需要的空間)是不一樣的。空間的單位可以是bit也可以是字節byte。
  

  除了這兩個計算架構相關的整數類型之外,還有8個可以顯式表達自身寬度的整數類型:
  

3、整數類型值的表示法

  

  如果以8進製爲變量num賦值:

num = 039 // 用"0"作爲前綴以表明這是8進製表示法

  如果以16進製爲變量num賦值:

num = 0x39

4、浮點數類型

  浮點數類型有兩個:float32/float64   浮點數類型的值一般由整數部分、小數點"."和小數部分組成。另外一種表示方法是在其中加入指數部分。指數部分由"E"或"e"以及帶正負號的10進制整數表示。例:3.9E-2表示浮點數0.039。3.9E+1表示浮點數39。
  有時候浮點數類型值也可以被簡化。比如39.0可以被簡化爲39。0.039可以被簡化爲.039。   在Go中浮點數的相關部分只能由10進製表示法表示。

5、複數類型

  複數類型有兩個:complex64和complex128。實際上,complex64類型的值會由兩個float32類型的值分別表示複數的實數部分和虛數部分。而complex128類型的值會由兩個float64類型的值表示複數的實數部分和虛數部分。
  負數類型的值一般由浮點數表示的實數部分、加號"+"、浮點數表示的虛數部分以及小寫字母"i"組成,比如3.9E+1 + 9.99E-2i。

6、byte與rune

  byte與rune都屬於別名類型。byte是uint8的別名類型,而rune是int32的別名類型。
  一個rune的類型值即可表示一個Unicode字符。一個Unicode代碼點通常由"U+"和一個以十六進制表示法表示的整數表示,例如英文字母'A'的Unicode代碼點爲"U+0041"。
  rune類型的值需要由單引號"'"包裹,不過我們還可以用另外幾種方式表示: 

  

  另外在rune類型值的表示中支持幾種特殊的字符序列,即:轉義符。如下圖: 

  

7、字符串類型

  字符串的表示法有兩種,即:原生表示法和解釋型表示法。原生表示法,需用用反引號"`"把字符序列包起來,如果用解釋型表示法,則需要用雙引號"""包裹字符序列。

var str1 string = "str"
var str1 string = `str`

  二者的區別是,前者表示的是所見即所得的(除了回車符)。後者所表示的值中轉義符會起作用。字符串值是不可變的,如果我們創建了一個此類型的值,就不可能再對它本身做任何修改。

三、高級數據類型

1、數組類型

  一個數組是可以容納若干相同類型的元素的容器。數組的長度是固定的。如下聲明一個數組類型:

type MyNumbers [3]int

類型聲明語句由關鍵字type、類型名稱和類型字面量組成

  上面這條類型聲明語句實際上是爲數組類型[3]int聲明瞭一個別名類型。這使得我們可以把MyNumbers當作數組類型[3]int來使用。
  我們表示這樣一個數組類型的值的時候。應該把該類型的類型字面量寫在最左邊,然後用花括號包裹該值包含的若干元素,各元素之間以(英文半角)逗號分割,即:

[3]int{1,2,3}

  現在我們把這個數組字面量賦給一個名爲numbers的變量:

var numbers = [3]int{1,2,3}

這是一條變量聲明語句,它在聲明變量的同時爲該變量賦值

  另一種方式是在其中的類型字面量中省略代表其長度的數組,例:

var numbers = [...]int{1,2,3}

  可以用如下方式訪問該變量中的任何一個元素。例:

numbers[0]
numbers[1]
numbers[2]

  如果要修改數組值中的某一個元素值,可以:

numbers[1] = 4

  可以用如下方式獲取數組長度:

var length = len(numbers)

  如果一個數組沒有賦值,則它的默認值爲[length]type{0,0,0...}

2、切片類型

(1) 基礎

  切片(slice)與數組一樣也是可以若干相同類型元素的容器。與數組不同的是切片類型的長度不確定。每個切片值都會將數組作爲其底層數據結構。表示切片類型的字面量如:

[]int

或者是:

[]string

  切片類型的聲明可以這樣:

type MySlice []int

  對切片值的表示也與數組值相似

[]int{1,2,3}

  操作數組值的方法同樣適用於切片值。還有一種操作數組的方式叫做“切片”,實施切片操作的方式就是切片表達式。例:

var number3 = [5]int{1,2,3,4,5}
var slice1 = numbers3[1:4]

  上例中切片表達式numbers3[1:4]的結果爲[]int{2,3,4}很明顯被切下的部分不包含元素上界索引指向的元素。實際上slice1這個切片值的底層數組正是number3的值。
  我們也可以在切片值上實施切片操作:

var slice2 = slice1[1:3]

  除了長度切片值以及數組值還有另外一個屬性--容量。數組的容量總是等於其長度,而切片值的容量往往與其長度不同。如下圖: 

  

  如圖所示,一個切片值的容量即爲它的第一個元素值在其底層數組中的索引值與該數組長度的差值的絕對值。可以使用cap()內建函數獲取數組、切片、通道類型的值的容量:

var capacity2 int = cap(slice2)

  切片類型屬於引用類型,它的零值即爲nil,即空值。如果我們只聲明瞭一個切片類型而不爲它賦值,則它的默認值:nil。

(2) 切片的更多操作方法

  有些時候我們可以在方括號中放入第三個正整數。如下圖所示:

numbers3[1:4:4]

  第三個正整數爲容量上界索引,它意義在於可以把作爲結果的切片值的容量設置的更小。它可以限制我們通過這個切片值對其底層數組中的更多元素的訪問。上節中numbers3和slice的賦值語句如下:

var numbers3 = [5]int{1,2,3,4,5}
var slice1 = numbers3[1:4]

  這時,變量slice1的值是[]int{2,3,4}。但是我們可以通過如下操作將其長度延展與其容量相同:

slice1 = slice1[:cap(slice1)]

  通過此操作,變量slice1的值變爲了[]int{2,3,4,5},且其長度和容量均爲4。現在number3的值中的索引值在(1,5)範圍內的元素都被體現在了slice1的值中。這是以number3的值是slice1的值的底層數組爲前提的。這意味着我們可以輕而易舉地通過切片訪問其底層數組中對應索引值更大的更多元素。如果我們編寫的函數返回了這樣一個切片值,那麼得到它的程序很可能會通過這種技巧訪問到本不應該暴露給它的元素。
  如果我們在切片中加入了第三個索引(即容量上限索引),如:

var slice1 = numbers3[1:4:4]

  那麼在此之後,我們將無法通過slice1訪問到number3的值中的第五個元素。
  雖然切片值在上述方面受到了其容量的限制。但是我們可以通過另外一種手段對其進行不受限制的擴展。這需要用到內建函數append。append會對切片值進行擴展並返回一個新的切片值,使用方法如下:

slice1 = append(slice1, 6, 7)

  通過上述操作,slice1的值變爲了[]int{2,3,4,6,7}。一旦擴展操作超出了被操作的切片值的容量,那麼該切片的底層數組就會被替換   最後一種操作切片的方式是“複製”。該操作的實施方法是調用copy函數。該函數接收兩個類型相同的切片值作爲參數,並把第二個參數值中的元素複製到第一個參數值中的相應位置(索引值相同)上。這裏有兩點需要注意:

  1. 這種複製遵循最小複製原則,即:被複制的元素的個數總是等於長度較短的那個參值的長度。
  2. 與append函數不同,copy函數會直接對其第一個參數值進行修改。

  例:

var slice4 = []int{0,0,0,0,0,0}
copy(slice4, slice1)

 通過上述複製操作,slice4會變成[]int{2,3,4,6,7,0,0}。

3、字典類型

  Go語言的字典(Map)類型是一個哈希表的實現。字典類型的字面量如下:

map[K]T

  其中,"K"爲鍵的類型,而"T"則代表元素(值)的類型。如果我們描述一個鍵類型爲int,值類型爲string的字典類型的話:

map[int]string

字典的鍵類型必須是可比較的,否則會引起錯誤,即鍵不能是切片、字典、函數類型

  字典值的字面量表示法實際上與數組的切片的字面量表示法很相似。最左邊仍然是類型字面量,右邊緊挨着由花括號包裹且有英文逗號分隔的鍵值對。每個鍵值對的鍵和值之間由冒號分隔。以字典類型map[int]string爲例。他的值的字面量可以是這樣的:

map[int]string{1:"a",2:"b"m,3:"c"}

  我們可以把這個值賦給一個變量

mm := map[int]string{1:"a",2:"b",3:"c"}

  可用索引表達式取出字典中的值:

b := mm[2]

  可以用索引表達式賦值:

mm[2] = b + "2"

  這樣mm中鍵爲2的值變爲了"b2"。可以用如下方式向字典中添加一個鍵值對:

mm[4] = ""

  對於字典值來說,如果指定鍵沒有對應的值則默認爲該類型的空值。所以mm[5]會返回一個""。但是這樣的話我們就不知道mm[5]到底是""還是mm[5]沒有這個值。所以go提供了另外一種寫法:

e, ok := mm[5]

  針對字典的索引表達式可以有兩個求職結果,第二個求職結果是bool類型的。它用於表明字典值中是否存在指定的鍵值對。   從字典中刪除鍵值對的方法非常簡單,僅僅是調用內建函數delete:

delete(mm, 4)

  無論mm中是否存在以4爲鍵的鍵值對,delete都刪除。   字典類型屬於引用類型,它的零值即爲nil

4、通道類型

  通道(Channel)是Go語言中一種非常獨特的數據結構。它可用於在不同Goroutine之間傳遞類型化的數據。並且是併發安全的。相比之下,之前幾種數據類型都不是併發安全的。
  Goroutine可以被看作是承載可被併發執行的代碼塊的載體。它們由Go語言的運行時系統調度,並依託操作系統線程(又稱內核線程)來併發地執行其中的代碼塊。
  通道類型的表示方法很簡單,僅由兩部分組成:

chan T

  在這個類型字面量中,左邊是代表通道類型的關鍵字chan,而右邊則是一個可變的部分,即代表該通道類型允許傳遞的數據的類型(或稱通道的元素類型)。
  與其他的數據類型不同,我們無法表示一個通道類型的值,因此,我們無法用字面量來爲通道類型的變量賦值。只能通過調用內建函數make來達到目的。make參數可接受兩個參數,第一個參數是代表了將被初始化的值的類型的字面量(例: chan int),而第二個參數則是值的長度,例如,若我們想要初始化一個長度爲5且元素類型爲int的通道值,則需要這樣寫:

make(chan int, 5)

  make函數也可以被用來初始化切片類型或字典類型的值。
  暫存在通道值中的數據是先進先出。下面,我們聲明一個通道類型的變量,併爲其賦值:

ch1 := make(chan string, 5)

  這樣一來,我們就可以使用接受操作符<-向通道值發送數據了。當然,也可以使用它從通道值接收數據,例如,如果我們要向通道ch1 發送字符串"value1",那麼應該這樣做:

ch1 <- "value1"

  如果我們從ch1那裏接收字符串,則要這樣:

<- ch1

  我們可以把接受到字符串賦給一個變量,如:

value := <- ch1

  與針對字典值的索引表達式一樣,針對通道值的接受操作也可以有第二個結果值:

value, ok := <- ch1

  這裏的ok的值是bool類型的。它代表了通道值的狀態,true代表通道值有效,而false則代表通道值已無效(或稱已關閉),更深層次的原因是,如果在接受操作進行之前或過程中通道值被關閉了,則接收操作會立即結束並返回一個該通道值的元素類型的零值。
  可以通過函數close來關閉通道:

close(ch1)

  對通道值的重複關閉會引發運行時異常,會使程序崩潰。在通道值有效的前提下,針對它的發送操作會在通道值已滿(其中緩存的數據的個數已等於它的長度)時被阻塞。而向一個已被關閉的通道值發送數據會引發運行時異常。針對有效通道值的接收操作會在它已經爲空時被阻塞。通道類型屬於引用類型,它的零值爲nil。

5、通道的更多種類

  上一節中的通道實際上只是Go中的通道的一種。是帶緩衝的通道,或者簡稱爲緩衝通道。
  通道有緩衝和非緩衝之分,緩衝通道中可以緩存N個數據,我們在初始化一個通道值的時候必須指定這個N,**相對的非緩衝通道不會緩存任何數據,發送方在向通道發送數據的時候會立即被阻塞。直到有一個接收方已從該通道中接收了這條數據。**非緩衝的通道值的初始化方法如下:

make(chan int, 0)

  除了上述分類方法,我們還可以以數據在通道中的傳輸方向爲依據來劃分通道。默認情況下,通道都是雙向的,即雙向通信。如果數據只能在通道中單向傳輸,那麼該通道被稱作單向通道。我們在初始化一個通道值的時候不能指定爲單向。但是在編寫類型聲明的時候可以:

type Receiver <- chan int

  類型Receiver代表了一個只從中接收數據的單向通道類型,這樣的通道也被稱爲接收通道。在關鍵字chan左邊的接收操作符<-表示了數據的流向。相對應的,如果我們想聲明一個發送通道類型,那麼應該這樣:

type Sender char <- int

  這次<-被放在了chan的右邊,並且“箭頭”直指“通道”。我們可以把一個雙向通道賦予上述類型的變量,就像這樣:

var myChannel = make(chan int, 3)
var sender Sender = myChannel
var receiver Receiver = myChannel

  但是反過來是不行的:

var myChannel1 chan int = sender

  單向通道的作用主要是約束程序對通道值的使用方式,比如,我們調用一函數時給予它一個發送通道作爲參數,以此來約束它只能向該通道發送數據。又比如,一個函數將一個接受通道作爲結果返回,以此來約束調用該函數的代碼只能從這個通道中接收數據。

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