1. 程式人生 > >Go語言學習:01-基本語法

Go語言學習:01-基本語法

[TOC] 基本語法 === 原始檔構成 --- 最簡單的一個go程式: ```go package main import "fmt" func main() { fmt.Println("Hello, World!") } ``` Go源程式由幾部分構成: 1. `package`用於包宣告:`package main`表示一個可獨立執行的程式,** Go應用程式必須包含名為`main`的包**,如果無`main`包,那麼編譯器會提示"cannot run non-main package"; 2. `import`用於匯入需要引用的外部包, `import "fmt"`告訴 Go 編譯器這個程式需要使用 fmt 包; 3. `func main()`是程式的主函式,一般來說都是在啟動後第一個執行的函式(如果有`init() `函式則會先執行`init`函式); 4. 識別符號由字母、數字和下劃線構成,首字元必須是字母或者下劃線,Go語言通過識別符號的首字母標識是否可以匯出到外部: - 當識別符號以大寫字母開頭,那麼這個識別符號就可以被外部包引用,也稱為匯出,類似於面嚮物件語言中的`public`; - 當識別符號以小寫字母開頭,則對包外是不可見的,類似於面嚮物件語言中的`protected`; 資料型別 --- 變數分為值型別和引用型別: - 值型別:int、float、bool 和 string 這些基本型別都屬於值型別,使用這些型別的變數直接指向記憶體中的值; - 引用型別:引用型別的變數儲存的是被引用物件的地址,也就是C語言中的指標。 ### 基本型別變數 Go語言中的變數型別基本涵蓋了C語言的基本型別,同時增加指定長度的型別,例如:int8、uint8、int16、uint16、int32、uint32等。 Go語言中有多種定義變數的方法,分別介紹: 1. 定義變數時指定資料型別,格式`var v_name v_type`,範例: ```go var varInPackage1 int // 本地變數,沒有賦初始值 var varInPackage2 int = 10 // 本地變數賦初始值為10 var VarInPackage int = 20 // 全域性變數賦初始值為20 ``` 2. 省略型別,讓編譯器根據右值推測資料型別: ```go var intVar = 10 var stringVar = "String" ``` 3. 省略var關鍵字,通過`:=`進行賦值,這種定義只能位於函式體內,用於建立區域性變數,所以必須確保變數**沒有**被定義過: ```go intVar := 10 intVar1, strVar = 20, "StrValue" // 同時定義兩個變數 a, _ = 10, 20 // 忽略第二個引數 ``` 4. 因式分解方式,一般用於宣告全域性變數: ```go var ( a int b bool ) ``` go語言中變數的作用域與C語言相同。 ### 陣列 陣列的定義: ```go var variable_name [SIZE] variable_type ``` 建立陣列格式: ```go var balance1 [10] int32 // 建立一個容量為10的陣列 var balance2 = [5]float32{1.0, 2.0, 3.4, 7.0, 5.1} // 建立一個容量為5的陣列,並且賦初始值 var balance3 = [...]float32{1.0, 2.1, 3.4, 7.5, 5.0} // 建立一個數組容量由後面定義的資料決定 ``` ### 切片 切片(slice)是對陣列一個連續片段的引用(該陣列我們稱之為相關陣列,通常是匿名的),所以切片是一個**引用型別**,切記不可用指標指向切片!! 和陣列不同的是,切片的長度可以在執行時修改,最小為 0 最大為相關陣列的長度:**切片是一個 長度可變的陣列**。 切片的宣告格式: ```go var identifier []type // 注意方括號為空 ``` #### 建立切片 切片有幾種建立方法: 1. 與特定陣列繫結,建立方法為: ```go var slice1 []type = arr1[start:end] // arr1為已經建立的陣列或者切片 ``` 2. 直接建立切片: ```go var x = []int{2, 3, 5, 7, 11} // 建立一個長度為5的切片 ``` 3. 使用make建立切片: ```go slice1 := make([]type, len) // len為切片的初始長度 slice1 := make([]type, len, cap) // cap為切片初始容量 ``` #### 調整容量 切片可以調整容量,下面的程式碼用於將`sl`的容量增加1: ```go sl = sl[0:len(sl)+1] ``` 下面的將去掉切片的第一個元素: ```go sl = sl[1:len(sl)] ``` #### 字串與切片 Go語言中字串是常量,不可變。 如果需要修改字串中的某個數字,則需要將字串轉化成切片後再進行修改。 例如,將字串 "hello" 轉換為 "cello": ```go s := "hello" c := []byte(s) c[0] = 'c' s2 := string(c) // s2 == "cello" ``` ### 常量 通過`const`關鍵字建立常量,常量的定義方法與變數類似,差異在於將`var`替換為`const`,範例: ```go const a string = "abc" const b = "abc" ``` 下面介紹一個特殊的常量`iota`,可以認為這個是在編譯前進由編譯器修改的常量。 在一個`const`組中,首次使用`iota`時值為0,每使用一次,`iota`自動加一,這種用法一般作為`enum`型別使用: ```go const ( a = iota // 首次呼叫,所以a=0 b // 不賦任何值時,就是使用與上一個變數相同的方式賦值,所以這個等價於 b = iota,即使不寫iota,編譯器預設就是自動呼叫 c // 等價於 c = iota ) const ( a1 = iota // 這是一個新的const組,iota初始值為0,所以a1=0 a2 // 等價於 a2 = iota ) ``` ### String Go語言提供了`strings`包,專門處理`string`型別的資料。 例如: - `strings.Contains()`判斷字串中是否包含特定字串; - `strings.Index()`返回特定字串在指定字串中的位置; - ... 文件可以參考:https://studygolang.com/pkgdoc ### Map Map 是一種無序的鍵值對的集合,Map 最重要的一點是通過 key 來快速檢索資料,key 類似於索引,指向資料的值。 Map 是一種集合,所以我們可以像迭代陣列和切片那樣迭代它,但是Map 是無序的,我們無法決定它的返回順序,這是因為 Map 是使用 hash 表來實現的。 map宣告方式: ```go var map_variable map[key_data_type]value_data_type ``` map 是 引用型別 的: 記憶體用 make 方法來分配,最簡單的初始化: ```go var map1 = make(map[keytype]valuetype) var map1 = make(map[keytype]valuetype, cap) // 指定容量 ``` 也可以帶初始值方式建立,例如: ```go mapCreated := map[string]float32{"C0": 16.35, "D0": 18.35} ``` 範例: ```go var kvs = make(map[string]string) // 建立一個Map kvs["AAA"] = "aaa"; kvs["BBB"] = "bbb"; for k,v := range kvs { // 遍歷Map fmt.Printf("\tK=%s, V=%s\n", k, v) } v,ret := kvs["CCC"] // 查詢map,第一個返回值是key對應的value,第二個返回值是結果 fmt.Printf("\tfind CCC, ret=%d, v=%s\n", ret, v) ``` 控制 --- ### 條件語句 #### if if語句格式與C語言相比,只不過測試表達式不帶括號(也可以帶括號),其他與C語言相同: ```go if 測試表達式 { /* 在布林表示式為 true 時執行 */ } else { /* 在布林表示式為 false 時執行 */ } ``` #### switch Go的`switch`與C的類似,與C的差異在於預設每個case後面自帶break語句,如果不需要`break`,則需要通過`fallthrough`關鍵字指明。 範例: ```go switch marks { case 90: grade = "A" case 80: grade = "B" case 50,60,70 : grade = "C" fallthrough // 表示沒有break,相當於接著執行default裡面的語句 default: grade = "D" } ``` `if`和`switch`還有另外一種寫法,支援接受一個初始化引數,格式如下: ```go if initstatement; condition {} switch initstatement; condition {} ``` 例如: ``` if err := file.Chmod(0664); err != nil ``` ### 迴圈語句 迴圈控制: ```go for init; condition; post { } // 與C語言相同 for condition { } // 等同於 while(condition) for { } // 等同於 for(;;) ``` `for`迴圈的`init/post`段不支援通過`,`分隔多個表示式,如果您需要初始化多個變數時,可以通過下面的方式: ```go sum := 0 for i,j := 0,100; i<=j; i++ { sum += i } ``` 迴圈遍歷array、slice、string、map時,可以使用`range`關鍵字進行控制,`range`第一個引數返回的是索引,第二個引數返回的是該索引位置的值: ```go strings := [] string{"string1", "string2", "string last"} for i,s :=range strings { fmt.Printf("\t%d = %s\n", i, s) } ``` 函式 --- ### 函式定義 標準函式定義如下: ```go func function_name([parameter list]) [return_types] { 函式體 } ``` 函式的引數同C語言一樣,存在值傳遞和引用傳遞,引用傳遞的方式與C語言的指標格式相同: ```go func swap(x *int, y *int) { var temp int temp = *x /* 保持 x 地址上的值 */ *x = *y /* 將 y 值賦給 x */ *y = temp /* 將 temp 值賦給 y */ } ``` 而實際上如果進行swap,還有更簡單的方式,例如下面是調換a、b兩個變數的值: ```go a, b = b, a ``` ### 函式變數 C語言中一般使用函式指標來指向一個函式,Go中可以直接將將函式賦值給變數,該變數就是函式: ```go // 建立一個函式變數 getSquareRoot getSquareRoot := func(x float64) float64 { return math.Sqrt(x) } fmt.Println(getSquareRoot(9)) /* 使用函式 */ ``` ### 閉包 所謂閉包,就是將函式自身使用的資料封裝到包中,對外不可見。 參考下面的函式,`getSequence`函式返回一個函式,被返回的函式中引用了`getSequence`函式的一個區域性變數,所以只要被返回的函式存在,那麼`getSequence`函中的區域性變數i就會存在,這個就是相當於將i變數封到了包中,即閉包。 ```go func getSequence() func() int { i:=0 return func() int { i+=1 // 這裡可以引用getSequence函式中定義的區域性變數 return i } } nextNumber := getSequence() // 建立閉包 ``` ### 多值返回 Go語言中一個函式可以返回一個或者多個值,多值返回函式範例: ```go func swap(x, y string) (string, string) { return y, x } ``` 也可以對返回值引數進行命名,這樣就可以在函式體中對返回值引數進行賦值,這個賦值就是相當於設定返回值,範例: ```go func f(x, y int) (sum int, sub int) { sum = x+y sub = x-y return } ``` ### defer 關鍵字`defer`允許我們推遲到函式返回之前執行,如果一個函式存在多個`defer`語句,那麼按照後進先出的順序執行,即棧的順序。 例如,下面的程式碼指定了2個Defer: ```go func deferFunc() { fmt.Printf("Hello here is defer funtion\n"); } func testDeferFunc() { fmt.Printf("print 1\n") defer deferFunc() defer func() { // 建立一個匿名函式,並defer執行 fmt.Printf("Hello here is defer function, inner\n") }() fmt.Printf("print 2\n") } ``` 輸出結果為: ``` print 1 print 2 Hello here is defer function, inner Hello here is defer funt