Go語言學習:01-基本語法
阿新 • • 發佈:2021-02-28
[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