Go語言基礎介紹
Go是一個開源的編程語言。Go語言被設計成一門應用於搭載Web服務器,存儲集群或類似用途的巨型中央服務器的系統編程語言。目前,Go最新發布版本為1.10.
Go語言可以運行在Linux、FreeBSD、Mac OS X和Windows系統上。
1. 結構:Go語言的基礎組成有以下幾個部分:包聲明、引入包、函數、變量、語句&表達式、註釋。
(1)、必須在源文件中非註釋的第一行指明這個文件屬於哪個包,如:package main
(2)、註釋與C++相同,有單行註釋即”//”和多行註釋即”/* … */”兩種。
(3)、當標識符(包括常量、變量、類型、函數名、結構字段等等)以一個大寫字母開頭,那麽使用這種形式的標識符的對象就可以被外部包的代碼所使用(客戶端程序需要先導入這個包),這被稱為導出;標識符如果以小寫字母開頭,則對包外是不可見的,但是它們在整個包的內部是可見並且可用的。
2. 基礎語法:
(1)、Go標記:Go程序可以由多個標記組成,可以是關鍵字、標識符、常量、字符串、字符。
(2)、行分隔符:在Go程序中,一行代表一個語句結束。每個語句不需要像C++語言一樣以分號”;”結尾,因為這些工作都將由Go編譯器自動完成。如果你打算將多個語句寫在同一行,它們必須使用”;”為區分,但在實際開發中我們並不鼓勵這種做法。
(3)、註釋:註釋不會被編譯,每一個包應該有相關註釋。單行註釋以”//”開頭,多行註釋以”/*”開頭,以”*/”結尾,與C++相同。
(4)、標識符:用來命名變量、類型等程序實體。一個標識符實際上就是一個或是多個字母(A~Z和a~z)、數字(0~9)、下劃線”_”組成的序列,但是第一個字符必須是字母或下劃線而不能是數字。標識符不能是Go語言的關鍵字。
(5)、關鍵字: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。36個預定義標識符:append、bool、type、cap、close、complex、complex64、complex128、uint16、copy、false、float32、float64、image、int、int8、int16、uint32、int32、int64、iota、len、make、new、nil、panic、unit64、print、println、real、recover、string、true、uint、uint8、uintptr。
程序一般由關鍵字、常量、變量、運算符、類型和函數組成。程序中可能會使用到這些分隔符:()、[]、{}。程序中可能會使用到這些標點符號:.、,、;、:和…。
3. 數據類型:在Go編程語言中,數據類型用於聲明函數和變量:布爾型(true、false);數字類型(整型、浮點型、復數);字符串類型(一串固定長度的字符連接起來的字符序列);派生類型(指針類型、數組類型、結構化類型、Channel類型、函數類型、切片類型、接口類型、Map類型)。
4. 變量:聲明變量的一般形式是使用var關鍵字。也可多變量聲明。
變量聲明方式:(1)、指定變量類型,聲明後若不賦值,使用默認值;(2)、根據值自行判定變量類型;(3)、省略var,註意:=左側的變量不應該是已經聲明過的,否則會導致編譯錯誤。
值類型和引用類型:所有像int、float、bool和string這些基本類型都屬於值類型,使用這些類型的變量直接指向存在內存中的值。你可以通過&i來獲取變量i的內存地址。值類型的變量的值存儲在棧中。內存地址會根據機器的不同而有所不同,甚至相同的程序在不同的機器上執行後也會有不同的內存地址。因為每臺機器可能有不同的存儲器布局,並且位置分配也可能不同。更復雜的數據通常會需要使用多個字,這些數據一般使用引用類型保存。一個引用類型的變量r1存儲的是r1的值所在的內存地址(數字),或內存地址中第一個字所在的位置。這個內存地址稱之為指針,這個指針實際上也被存在另外的某一個字中。同一個引用類型的指針指向的多個字可以是連續的內存地址中(內存布局是連續的),也可以將這些字分散存放在內存中,每個字都指示了下一個字所在的內存地址。
簡短形式,使用:=賦值操作符:我們知道可以在變量的初始化時省略變量的類型而由系統自動推斷,聲明語句寫上var關鍵字其實是顯得多余了,因此我們可以將它們簡寫為:a:= 5 或b := false,a和b的類型(int和bool)將由編譯器自動推斷。這是使用變量的首選形式,但是它只能被用在函數體內,而不可以用於全局變量的聲明與賦值。
註意事項:(1)、如果在相同的代碼塊中,我們不可以再次對於相同名稱的變量使用初始化聲明。(2)、如果你聲明了一個局部變量卻沒有在相同的代碼塊中使用它,同樣會得到編譯錯誤。但是全局變量是允許聲明但不使用。同一類型的多個變量可以聲明在同一行。
空白標識符_也被用於拋棄值。_實際上是一個只寫變量,你不能得到它的值。這樣做是因為Go語言中你必須使用所有被聲明的變量,但有時你並不需要使用從一個函數得到的所有返回值。
多個變量可以聲明在同一行,多個變量也可以在同一行進行賦值,這被稱為並行或同時賦值。並行賦值也被用於當一個函數返回多個返回值時。
5. 常量:是一個簡單值的標識符,在程序運行時,不會被修改的量。常量中的數據類型只可以是布爾型、數字型(整數型、浮點型和復數)和字符串型。
你可以省略類型說明符,因為編譯器可以根據變量的值來推斷其類型。也可以在同一行聲明多個常量和賦值。常量還可以用作枚舉。
常量可以用len()、cap()、unsafe.Sizeof()函數計算表達式的值。常量表達式中,函數必須是內置函數。
iota:特殊常量,可以認為是一個可以被編譯器修改的常量。在每一個const關鍵字出現時,被重置為0,然後再下一個const出現之前,每出現一次iota,其所代表的數字會自動增加1。iota可以被用作枚舉值。
6. 運算符:用於在程序運行時執行數學或邏輯運算。
Go語言內置的運算符有:算術運算符、關系運算符、邏輯運算符、位運算符、賦值運算符、其它運算符。
算術運算符:+、-、*、/、%、++、--
關系運算符:==、!=、>、<、>=、<=
邏輯運算符:&&、||、!
位運算符:&、|、^、<<、>>,對整數在內存中的二進制位進行操作。
賦值運算符:=、+=、-=、*=、/=、%=、<<=、>>=、&=、^=、|=
其它運算符:&(獲取變量存儲地址)、*(指針變量)
運算符優先級:二元運算符的運算方向均是從左至右。可以使用括號來臨時提升某個表達式的整體運算優先級。
7. 條件語句:需要開發者通過指定一個或多個條件,並通過測試條件是否為true來決定是否執行指定語句,並在條件為false的情況下執行另外的語句。
if語句:由布爾表達式後緊跟一個或多個語句組成。if語句後可以使用可選的else語句。你也可以在if或else if語句中嵌入一個或多個if或else if語句。
switch語句:用於基於不同條件執行不同動作,每一個case分支都是唯一的,從上到下逐一檢查,直到匹配為止。匹配項後面也不需要再加break。case類型不被局限於常量或整數,但必須是相同的類型。switch語句還可以被用於type--switch來判斷某個interface變量中實際存儲的變量類型。
select語句:select是Go中的一個控制結構,類似於用於通信的switch語句。每個case必須是一個通信操作,要麽是發送要麽是接收。select隨機執行一個可運行的case。如果沒有case可運行,它將阻塞,直到有case可運行。一個默認的子句應該總是可運行的。select語句的語法:(1)、每個case都必須是一個通信;(2)、所有channel表達式都會被求值;(3)、所有被發送的表達式都會被求值;(4)、如果任意某個通信可以進行,它就執行,其它被忽略;(5)、如果有多個case都可以運行,select會隨機公平地選出一個執行,其它不會執行。否則,如果有default子句,則執行該語句。如果沒有default子句,select將阻塞,直到某個通信可以運行。Go不會重新對channel或值進行求值。
8. 循環語句:for,可以嵌套一個或多個for。循環控制語句可以控制循環體內語句的執行過程,包括break、continue、goto。如果循環中條件語句永遠不為false則會進行無限循環。有些類似於C語言中的for和while。
break語句:(1)、用於循環語句中跳出循環,並開始執行循環之後的語句;(2)、在switch中在執行一條case後跳出語句的作用。
continue語句:有點像break語句,但是continue不是跳出循環,而是跳過當前循環執行下一次循環語句。
goto語句:可以無條件地轉移到過程中指定的行。在結構化程序設計中一般不主張使用goto語句。
9. 函數:是基本的代碼塊,用於執行一個任務。Go語言最少有個main()函數。函數聲明告訴了編譯器函數的名稱、返回類型和參數。Go語言標準庫提供了多種可動用的內置的函數。
函數定義格式:func function_name([parameter list]) [return_types] {函數體}
函數定義解析:(1)、func:函數由func開始聲明。(2)、function_name:函數名稱,函數名和參數列表一起構成了函數簽名。(3)、parameter list:參數列表,參數就像一個占位符,當函數被調用時,你可以將值傳遞給參數,這個值被稱為實際參數。參數列表指定的是參數類型、順序、及參數個數。參數是可選的,函數也可以不包含參數。(4)、return_types:返回類型,函數返回一列值。(5)、函數體:函數定義的代碼集合。
Go函數可以返回多個值。
函數參數:函數如果使用參數,該變量可稱為函數的形參。形參就像定義在函數體內的局部變量。調用函數,可以通過兩種方式傳遞參數:值傳遞和引用傳遞。默認情況下,Go語言使用的是值傳遞,即在調用過程中不會影響到實際參數。
值傳遞:是指在調用函數時將實際參數復制一份傳遞到函數中,這樣在函數中如果對參數進行修改,將不會影響到實際參數。
引用傳遞:是指在調用函數時將實際參數的地址傳遞到函數中,那麽在函數中對參數所進行的修改,將影響到實際參數。
函數用法:(1)、函數作為值:函數定義後可作為值來使用。(2)、閉包:是匿名函數,可在動態編程中使用。匿名函數是一個”內聯”語句或表達式。匿名函數的優越性在於可以直接使用函數內的變量,不必聲明。(3)、方法:是一個包含了接受者的函數,接受者可以是命名類型或者結構體類型的一個值或者是一個指針。所有給定類型的方法屬於該類型的方法集。
10. 變量作用域:Go語言中變量可以在三個地方聲明:局部變量、全局變量、形式參數。
(1)、局部變量:在函數體內聲明的變量稱之為局部變量,它們的作用域只在函數體內,參數和返回值變量也是局部變量。
(2)、全局變量:在函數體外聲明的變量稱之為全局變量,全局變量可以在整個包甚至外部包(被導出後)使用。全局變量可以在任何函數中使用。
(3)、形式參數:函數定義中的變量稱為形式參數。形式參數會作為函數的局部變量來使用。
Go語言程序中全局變量與局部變量名稱可以相同,但是函數內的局部變量會被優先考慮。
11. 數組:是具有相同唯一類型的一組已編號且長度固定的數據項序列,這種類型可以是任意的原始類型如整型、字符串或者自定義類型。數組元素可以通過索引(位置)來讀取(或者修改),索引從0開始,第一個元素索引為0,第二個索引為1,以此類推。
聲明數組:Go語言數組聲明需要指定元素類型及元素個數。數組長度必須是整數且大於0。
初始化數組:初始化數組中{}中的元素個數不能大於[]中的數字。如果忽略[]中的數字不設置數字大小,Go語言會根據元素的個數來設置數組的大小。
訪問數組元素:數組元素可以通過索引(位置)來讀取。格式為數組名後加中括號,中括號中為索引的值。
多維數組:Go語言支持多維數組。
向函數傳遞數組:如果你想向函數傳遞數組參數,你需要在函數定義時,聲明形參為數組。
12. 指針:變量是一種方便的占位符,用於引用計算機內存地址。Go語言的取地址符是&,放到一個變量前使用就會返回相應變量的內存地址。一個指針變量指向了一個值的內存地址。
指針使用流程:定義指針變量;為指針變量賦值;訪問指針變量中指向地址的值。在指針類型前面加上*號(前綴)來獲取指針所指向的內容。
空指針:當一個指針被定義後沒有分配到任何變量時,它的值為nil。nil指針也為空指針。nil在概念上和其它語言的null、None、nil、NULL一樣,都指代零值或空值。
指針數組:可以定義一個指針數組來存儲地址。
指向指針的指針:如果一個指針變量存放的又是另一個指針變量的地址,則稱這個指針變量為指向指針的指針變量。訪問指向指針的指針變量值需要使用兩個*號。
向函數傳遞指針參數:只需要在函數定義的參數上設置為指針類型即可。通過引用或地址傳參,在函數調用時可以改變其值。
13. 結構體:Go語言中數組可以存儲同一類型的數據,但在結構體中我們可以為不同項定義不同的數據類型。結構體是由一系列具有相同類型或不同類型的數據構成的數據集合。
結構體定義需要使用type和struct語句。struct語句定義一個新的數據類型,結構體中有一個或多個成員。type語句設定了結構體的名稱。一旦定義了結構體類型,它就能用於變量的聲明。如果要訪問結構體成員,需要使用點號(.)操作符。你可以像其它數據類型一樣將結構體類型作為參數傳遞給函數。你可以定義指向結構體的指針類似於其它指針變量。
14. 切片(slice):Go語言切片是對數組的抽象。Go數組的長度不可改變,Go中提供了一種靈活,功能強悍的內置類型切片(“動態數組”)。與數組相比切片的長度是不固定的,可以追加元素,在追加時可能使切片的容量增大。有些類似於C++中的vector。
你可以聲明一個未指定大小的數組來定義切片,切片不需要說明長度,或使用make()函數來創建切片。
切片是可索引的,並且可以用len()方法獲取長度。切片提供了計算容量的方法cap()可以測量切片最長可以達到多少。一個空切片在未初始化之前默認為nil,長度為0.可以通過設置下限及上限來設置截取切片。
如果想增加切片的容量,我們必須創建一個新的更大的切片並把原分片的內容都拷貝過來。可使用拷貝切片的copy方法,或向切片追加新元素的append方法。
15. range關鍵字:Go語言中range關鍵字用於for循環中叠代數組(array)、切片(slice)、通道(channel)或集合(map)的元素。在數組和切片中它返回元素的索引值,在集合中返回key-value對的key值。
16. Map(集合):Map是一種無序的鍵值對的集合。Map最重要的一點是通過key來快速檢索數據,key類似於索引,指向數據的值。Map是一種集合,所以我們可以像叠代數組和切片那樣叠代它。Map是無序的,我們無法決定它的返回順序,這是因為Map是使用hash表來實現的。
可以使用內建函數make也可以使用map關鍵字來定義Map。如果不初始化map,那麽就會創建一個nil map。nil map不能用來存放鍵值對。delete()函數用來刪除集合的元素。
17. 遞歸函數:遞歸,就是在運行的過程中調用自己。在使用遞歸時,需要設置退出條件,否則遞歸將陷入無限循環中。
18. 類型轉換:用於將一種數據類型的變量轉換為另外一種類型的變量,形式為:type_name(expression)
19. 接口:把所有的具有共性的方法定義在一起,任何其它類型只要實現了這些方法就是實現了這個接口。
20. 錯誤處理:Go語言通過內置的錯誤接口提供了非常簡單的錯誤處理機制。
以下是測試代碼:
[plain] view plain copy- // 變量相關測試代碼
- package main
- import "fmt"
- // 全局變量聲明
- var c float32
- var x = 11
- func main() {
- // 局部變量聲明
- // 指定變量類型,聲明後若不賦值,使用默認值
- var available bool
- available = true
- fmt.Println(available)
- // 根據值自動判定變量類型
- var a = 1
- var b = 2.5
- fmt.Println(a, b)
- c = float32(a) + float32(b)
- fmt.Printf("c = %f\n", c)
- // 省略var, 註意 :=左側的變量不應該是已經聲明過的,否則會導致編譯錯誤,這種形式只能被用在函數體中
- valid := false
- _, d := 5, 6 // 空白標識符
- fmt.Println(valid, d)
- // 多變量聲明
- var enabled, disabled = true, "111"
- fmt.Println(enabled, disabled)
- // 獲取變量a的內存地址
- fmt.Println(&a)
- // Go語言程序中全局變量與局部變量名稱可以相同,但是函數內的局部變量會被優先考慮
- var x = -11
- fmt.Printf("x = %d\n", x)
- }
- // 常量相關測試代碼
- package main
- import "fmt"
- import "unsafe"
- // 常量還可以用作枚舉
- const (
- o = "abc"
- p = len(o)
- q = unsafe.Sizeof(o)
- )
- // itoa: iota可以被用作枚舉值,第一個iota等於0,每當iota在新的一行被使用時,它的值都會自動加1
- const (
- d = iota
- e
- f
- )
- func main() {
- const a string = "abc" // 顯示類型定義
- const b = "def" // 隱式類型定義
- fmt.Println(a, b)
- const x, y, z = 1, 0.2, "blog"
- fmt.Println(x, y, z)
- fmt.Println(o, p, q)
- fmt.Println(d, e, f)
- }
- // 運算符相關測試代碼
- package main
- import "fmt"
- func main() {
- // 算術運算符
- var a, b, c = 1, 2, 3
- fmt.Printf("a + b = %d\n", a+b)
- fmt.Printf("a - c = %d\n", a-c)
- fmt.Printf("a * b = %d\n", a*b)
- c++ // 註:++和--好像僅有前綴運算符
- fmt.Printf("c++ = %d\n", c)
- b--
- fmt.Printf("--b = %d\n", b)
- // 關系運算符
- if a > b { // 註:visual studio code會默認把if (a > b)調整為if a > b
- fmt.Printf("a > b\n")
- } else {
- fmt.Printf("a <= b\n")
- }
- // 邏輯運算符
- x, y := true, false
- if x && y {
- fmt.Printf("true\n")
- } else {
- fmt.Printf("false\n")
- }
- // 位運算符: 好像僅支持整數
- a, b = 1, 3
- c = a & b
- fmt.Printf("c = %d\n", c)
- fmt.Printf("b << 2: %d\n", b<<2)
- // 賦值運算符
- b <<= 2
- fmt.Printf("b = %d\n", b)
- // 其它運算符:&、*
- fmt.Printf("b‘s address: %0x\n", &b)
- var ptr *int
- ptr = &a
- fmt.Printf("pointer: %0x\n", ptr)
- }
- // 條件語句相關測試代碼
- package main
- import "fmt"
- func main() {
- // if
- a := 1.2
- if a > 0 {
- fmt.Printf("a > 0, a = %f\n", a)
- }
- // if else
- b := 2.5
- if b > 10 {
- fmt.Printf("b > 10\n")
- } else {
- fmt.Printf("b <= 10\n")
- }
- fmt.Printf("b = %f\n", b)
- // if 嵌套
- if a > 0 {
- if b > 0 {
- fmt.Printf("a > 0, b > 0\n")
- }
- }
- // switch
- grade := "B"
- marks := 90
- switch marks {
- case 90:
- grade = "A"
- case 80:
- grade = "B"
- case 50, 60:
- grade = "C"
- default:
- grade = "D"
- }
- fmt.Printf("grade = %s\n", grade)
- // type switch
- var x interface{}
- switch i := x.(type) {
- case nil:
- fmt.Printf(" x 的類型: %T\n", i)
- case int:
- fmt.Printf("x 是 int 型\n")
- case float64:
- fmt.Printf("x 是 float64 型\n")
- case func(int) float64:
- fmt.Printf("x 是 func(int) 型\n")
- case bool, string:
- fmt.Printf("x 是 bool 或 string 型\n")
- default:
- fmt.Printf("未知型\n")
- }
- // select
- var c1, c2, c3 chan int
- var i1, i2 int
- select {
- case i1 = <-c1:
- fmt.Printf("received ", i1, " from c1\n")
- case c2 <- i2:
- fmt.Printf("sent ", i2, " to c2\n")
- case i3, ok := (<-c3): // same as: i3, ok := <-c3
- if ok {
- fmt.Printf("received ", i3, " from c3\n")
- } else {
- fmt.Printf("c3 is closed\n")
- }
- default:
- fmt.Printf("no communication\n")
- }
- }
- // 循環語句相關測試代碼
- package main
- import "fmt"
- func main() {
- // for
- a, b := 0, 15
- fmt.Printf("a的值為: ")
- for a = 0; a < 10; a++ {
- fmt.Printf(" %d ", a)
- }
- fmt.Printf("\n")
- fmt.Printf("a的值為: ")
- for a < b {
- a++
- fmt.Printf(" %d ", a)
- }
- fmt.Printf("\n")
- // for嵌套、break
- var i, j int
- fmt.Printf("2到100之間的素數包括:")
- for i = 2; i < 100; i++ {
- for j = 2; j <= (i / j); j++ {
- if i%j == 0 {
- break
- }
- }
- if j > (i / j) {
- fmt.Printf(" %d ", i)
- }
- }
- fmt.Printf("\n")
- // continue
- a = 0
- fmt.Printf("a的奇數:")
- for a < 20 {
- a++
- if a%2 == 0 {
- continue
- }
- fmt.Printf(" %d ", a)
- }
- fmt.Printf("\n")
- // goto
- a = 0
- fmt.Printf("打印a的值:")
- Loop:
- for a < 10 {
- if a == 5 {
- a++
- goto Loop
- }
- fmt.Printf(" %d ", a)
- a++
- }
- fmt.Printf("\n")
- }
- // 函數相關測試代碼
- package main
- import (
- "fmt"
- "math"
- )
- // 函數返回兩個數的最大值,值傳遞
- func max(num1, num2 int) int {
- var result int
- if num1 > num2 {
- result = num1
- } else {
- result = num2
- }
- return result
- }
- // 函數支持返回多個值,值傳遞
- func swap(x, y string) (string, string) {
- return y, x
- }
- // Go函數不支持C++中的函數重載,引用傳遞
- func swap1(x *int, y *int) {
- var temp int
- temp = *x
- *x = *y
- *y = temp
- }
- func getSequence() func() int {
- i := 0
- return func() int {
- i++
- return i
- }
- }
- type Circle struct {
- radius float64
- }
- func (c Circle) getArea() float64 {
- // c.radius 即為Circle類型對象中的屬性
- return 3.14 * c.radius * c.radius
- }
- func main() {
- a, b := 100, 200
- var ret = max(a, b)
- fmt.Printf("a,b最大值為: %d\n", ret)
- var x, y = "hello", "beijing"
- fmt.Printf("x: %s, y: %s\n", x, y)
- m, n := swap(x, y)
- fmt.Printf("x: %s, y: %s\n", x, y)
- fmt.Printf("m: %s, n: %s\n", m, n)
- p, q := -100, 500
- fmt.Printf("p: %d, q: %d\n", p, q)
- swap1(&p, &q)
- fmt.Printf("p: %d, q: %d\n", p, q)
- // 函數用法:函數作為值
- getSquareRoot := func(x float64) float64 {
- return math.Sqrt(x)
- }
- fmt.Printf("square root: %f\n", getSquareRoot(9))
- // 函數用法:閉包
- nextNumber := getSequence()
- // 調用nextNumber函數,i 變量自增 1 並返回
- fmt.Println("i的值: %d", nextNumber())
- fmt.Println("i的值: %d", nextNumber())
- fmt.Println("i的值: %d", nextNumber())
- // 創建新的函數nextNumber1,並查看結果
- nextNumber1 := getSequence()
- fmt.Println("i的值: %d", nextNumber1())
- fmt.Println("i的值: %d", nextNumber1())
- // 函數用法:方法
- var c1 Circle
- c1.radius = 10.00
- fmt.Println("Area of Circle(c1) = ", c1.getArea())
- }
- // 數組相關測試代碼
- package main
- import "fmt"
- func getAverage(arr []int, size int) float32 {
- var avg, sum float32
- for i := 0; i < size; i++ {
- sum += float32(arr[i])
- }
- avg = sum / float32(size)
- return avg
- }
- func main() {
- // 一維數組
- const num = 10
- var n [num]int
- for i := 0; i < num; i++ {
- n[i] = i + 100
- }
- for j := 0; j < num; j++ {
- fmt.Printf("Element[%d] = %d\n", j, n[j])
- }
- // 初始化數組
- const num2 = 5
- var m = [num2]float32{0., 1., 2., 3., 4.}
- for i := 0; i < num2; i++ {
- fmt.Printf("Element[%d] = %f\n", i, m[i])
- }
- // 初始化二維數組並訪問
- var a = [3][4]int{{0, 1, 2, 3}, {4, 5, 6, 7}, {8, 9, 10, 11}}
- fmt.Printf("a[2][3]: %d\n", a[2][3])
- // 向函數傳遞數組
- var balance = []int{1000, 2, 3, 17, 50}
- avg := getAverage(balance, 5)
- fmt.Printf("平均值:%f\n", avg)
- }
- // 指針相關測試代碼
- package main
- import "fmt"
- const MAX int = 3
- func swap(x *int, y *int) {
- var temp int
- temp = *x
- *x = *y
- *y = temp
- }
- func main() {
- // 指針常規操作
- var a = 20
- var ip *int
- ip = &a
- fmt.Printf("a 變量的地址是: %x\n", &a)
- fmt.Printf("ip 變量儲存的指針地址: %x\n", ip)
- fmt.Printf("*ip 變量的值: %d\n", *ip)
- var ptr *int
- fmt.Printf("ptr 的值為 : %x\n", ptr)
- if ptr == nil {
- fmt.Printf("ptr為空指針\n")
- }
- // 指針數組
- b := []int{10, 100, 200}
- var ptr2 [MAX]*int
- for i := 0; i < MAX; i++ {
- ptr2[i] = &b[i]
- }
- for i := 0; i < MAX; i++ {
- fmt.Printf("b[%d] = %d\n", i, *ptr2[i])
- }
- // 指向指針的指針
- var pptr **int
- pptr = &ip
- fmt.Printf("指向指針的指針變量 **pptr = %d\n", **pptr)
- // 向函數傳遞指針參數
- x, y := -11, 22
- fmt.Printf("x = %d, y = %d\n", x, y)
- swap(&x, &y)
- fmt.Printf("x = %d, y = %d\n", x, y)
- }
- // 結構體相關測試代碼
- package main
- import "fmt"
- type Books struct {
- title string
- author string
- subject string
- book_id int
- }
- func main() {
- // 訪問結構體成員
- var Book1, Book2 Books
- Book1 = Books{"Go語言", "www.runoob.com", "Go語言教程", 649507}
- Book2.title = "Python 教程"
- Book2.author = "www.runoob.com"
- Book2.subject = "Python 語言教程"
- Book2.book_id = 6495700
- fmt.Printf("Book 1 title: %s, autor: %s, subject: %s, book_id: %d\n",
- Book1.title, Book1.author, Book1.subject, Book1.book_id)
- fmt.Printf("Book 2 title: %s, autor: %s, subject: %s, book_id: %d\n",
- Book2.title, Book2.author, Book2.subject, Book2.book_id)
- // 結構體作為函數參數
- printBook(Book1)
- printBook(Book2)
- // 結構體指針
- printBook2(&Book1)
- printBook2(&Book2)
- }
- func printBook(book Books) {
- fmt.Printf("Book 1 title: %s, autor: %s, subject: %s, book_id: %d\n",
- book.title, book.author, book.subject, book.book_id)
- }
- func printBook2(book *Books) {
- fmt.Printf("Book 1 title: %s, autor: %s, subject: %s, book_id: %d\n",
- book.title, book.author, book.subject, book.book_id)
- }
- // 切片(slice)相關測試代碼
- package main
- import "fmt"
- func main() {
- // 定義切片
- //var slice1 []int
- //slice2 := make([]int, 5)
- //slice3 := make([]int, 5, 10)
- // 切片初始化
- //slice4 := []int{1, 2, 3}
- //var arr = [5]float32{0., 1., 2., 3., 4.}
- //slice5 := arr[:]
- // len()和cap()函數
- var numbers = make([]int, 3, 5)
- printSlice(numbers)
- // 空(nil)切片
- var number2 []int
- printSlice(number2)
- // 切片截取
- numbers3 := []int{0, 1, 2, 3, 4, 5, 6, 7, 8}
- printSlice(numbers3)
- fmt.Println("numbers3 ==", numbers3)
- // 打印子切片從索引1(包含) 到索引4(不包含)
- fmt.Println("numbers3[1:4] ==", numbers3[1:4])
- // 默認下限為
- fmt.Println("numbers3[:3] ==", numbers3[:3])
- // 默認上限為 len(s)
- fmt.Println("numbers3[4:] ==", numbers3[4:])
- // 增加切片的容量
- var numbers4 []int
- printSlice(numbers4)
- // 允許追加空切片
- numbers4 = append(numbers4, 0)
- printSlice(numbers4)
- // 向切片添加一個元素
- numbers4 = append(numbers4, 1)
- printSlice(numbers4)
- // 同時添加多個元素
- numbers4 = append(numbers4, 2, 3, 4)
- printSlice(numbers4)
- // 創建切片 numbers5是之前切片的兩倍容量
- numbers5 := make([]int, len(numbers4), (cap(numbers4))*2)
- printSlice(numbers5)
- // 拷貝numbers4的內容到numbers5
- copy(numbers5, numbers4)
- printSlice(numbers5)
- }
- func printSlice(x []int) {
- fmt.Printf("len=%d cap=%d slice=%v\n", len(x), cap(x), x)
- }
- // 範圍(range)相關測試代碼
- package main
- import "fmt"
- func main() {
- // 使用range去求一個slice的和,使用數組跟這個很類似
- nums := []int{2, 3, 4}
- sum := 0
- for _, num := range nums {
- sum += num
- }
- fmt.Printf("sum: %d\n", sum)
- // 在數組上使用range將傳入index和值兩個變量,上面那個例子我們不需要使用該元素的序號,所以我們使用空白符"_"省略
- for i, num := range nums {
- if num == 3 {
- fmt.Printf("index: %d\n", i)
- }
- }
- // range也可以用在map的鍵值對上
- kvs := map[string]string{"a": "apple", "b": "banana"}
- for k, v := range kvs {
- fmt.Printf("%s -> %s\n", k, v)
- }
- // range也可以用來枚舉Unicode字符串. 第一個參數是字符的索引,第二個是字符(Unicode的值)本身
- for i, c := range "go" {
- fmt.Println(i, c)
- }
- }
- // Map(集合)相關測試代碼
- package main
- import "fmt"
- func main() {
- // 創建map
- var countryCapitalMap map[string]string
- countryCapitalMap = make(map[string]string)
- // map插入key-value對,各個國家對應的首都
- countryCapitalMap["France"] = "Paris"
- countryCapitalMap["Italy"] = "Rome"
- countryCapitalMap["Japan"] = "Tokyo"
- countryCapitalMap["India"] = "New Delhi"
- // 使用key輸出map值
- for country := range countryCapitalMap {
- fmt.Printf("Capital of %s is %s\n", country, countryCapitalMap[country])
- }
- // 查看元素在集合中是否存在
- captial, ok := countryCapitalMap["United States"]
- // 如果ok是true,則存在,否則不存在
- if ok {
- fmt.Println("Capital of United States is", captial)
- } else {
- fmt.Println("Capital of United States is not present")
- }
- // 創建map
- countryCapitalMap2 := map[string]string{"France": "Paris", "Italy": "Rome", "Japan": "Tokyo", "India": "New Delhi"}
- fmt.Println("原始 map:")
- for country := range countryCapitalMap2 {
- fmt.Println("Capital of", country, "is", countryCapitalMap2[country])
- }
- // 刪除元素
- delete(countryCapitalMap2, "France")
- fmt.Println("刪除元素後 map:")
- for country := range countryCapitalMap2 {
- fmt.Println("Capital of", country, "is", countryCapitalMap2[country])
- }
- }
- // 遞歸相關測試代碼
- package main
- import "fmt"
- func Factorial(n uint64) (result uint64) {
- if n > 0 {
- result = n * Factorial(n-1)
- return result
- }
- return 1
- }
- func fibonacci(n int) int {
- if n < 2 {
- return n
- }
- return fibonacci(n-2) + fibonacci(n-1)
- }
- func main() {
- // 階乘
- var i = 15
- fmt.Printf("%d 的階乘是 %d\n", i, Factorial(uint64(i)))
- // 斐波那契數列
- for i = 0; i < 10; i++ {
- fmt.Printf("%d\t", fibonacci(i))
- }
- fmt.Println()
- }
- // 接口相關測試代碼
- package main
- import "fmt"
- type Phone interface {
- call()
- }
- type NokiaPhone struct {
- }
- func (nokiaPhone NokiaPhone) call() {
- fmt.Println("I am Nokia, I can call you!")
- }
- type IPhone struct {
- }
- func (iPhone IPhone) call() {
- fmt.Println("I am iPhone, I can call you!")
- }
- func main() {
- var phone Phone
- phone = new(NokiaPhone)
- phone.call()
- phone = new(IPhone)
- phone.call()
- }
- // 錯誤處理相關測試代碼
- package main
- import "fmt"
- type DivideError struct {
- dividee int
- divider int
- }
- // 實現‘error‘接口
- func (de *DivideError) Error() string {
- strFormat := `
- Cannot proceed, the divider is zero.
- dividee: %d
- divider: 0
- `
- return fmt.Sprintf(strFormat, de.dividee)
- }
- func Divide(varDividee int, varDivider int) (result int, errorMsg string) {
- if varDivider == 0 {
- dData := DivideError{varDividee, varDivider}
- errorMsg = dData.Error()
- return
- } else {
- return varDividee / varDivider, ""
- }
- }
- func main() {
- // 正常情況
- if result, errorMsg := Divide(100, 10); errorMsg == "" {
- fmt.Println("100/10 = ", result)
- }
- // 當被除數為零的時候會返回錯誤信息
- if _, errorMsg := Divide(100, 0); errorMsg != "" {
- fmt.Println("errorMsg is: ", errorMsg)
- }
- }
以上內容摘自:runoob.com
GitHub: https://github.com/fengbingchun/Go_Test
Go語言基礎介紹