Go 編程快速入門學習

0x00 Go 語言基礎之變量和常量

0x01 Go 語言基礎之基本數據類型

0x02 Go 語言基礎之運算符

0x03 Go 語言基礎之流程控制

0x00 Go 語言基礎之變量和常量

描述: 變量和常量是編程中必不可少的部分,也是很好理解的一部分。

1. 標識符

描述: 在編程語言中標識符就是程序員定義的具有特殊意義的詞,比如變量名、常量名、函數名等等。 Go 語言中標識符由字母數字和_(下劃線)組成,並且只能以字母和_開頭。 舉幾個例子:abc, _, _123, a123

編程語言中常用的三種命名規則,而 Go 語言推薦使用駝峯法式命名。

# 下劃線連接
student_name

# 小駝峯法式 (推薦方式)
studentName

# 大駝峯法式
StudentName

2. 關鍵字

描述: 關鍵字是指編程語言中預先定義好的具有特殊含義的標識符。 關鍵字和保留字都不建議用作變量名。

* var const      變量和常量的聲明
* var varName type  或者 varName : = value
* package and import: 導入
* func   用於定義函數和方法
* return 用於從函數返回
* defer someCode 在函數退出之前執行
* go :      用於並行
* select    用於選擇不同類型的通訊
* interface 用於定義接口
* struct    用於定義抽象數據類型
* breakcasecontinueforfallthroughelseifswitchgotodefault 流程控制
* chan  用於channel通訊
* type  用於聲明自定義類型
* map   用於聲明map類型數據
* range 用於讀取slicemapchannel數據
# Constants: 
true  false  iota  nil

# Types:    
int  int8  int16  int32  int64  
uint  uint8  uint16  uint32  uint64  uintptr
float32  float64  complex128  complex64
bool  byte  rune  string  error

# Functions:   
make  len  cap  new  append  copy  close  delete
complex  real  imag
panic  recover

3. 變量 - Var

Q: 變量的來歷?

答: 程序運行過程中的數據都是保存在內存中,我們想要在代碼中操作某個數據時就需要去內存上找到這個變量,但是如果我們直接在代碼中通過內存地址去操作變量的話,代碼的可讀性會非常差而且還容易出錯,所以我們就利用變量將這個數據的內存地址保存起來,以後直接通過這個變量就能找到內存上對應的數據了。

Q: 變量類型

答: 變量(Variable)的功能是存儲數據。不同的變量保存的數據類型可能會不一樣。經過半個多世紀的發展,編程語言已經基本形成了一套固定的類型,常見變量的數據類型有:整型、浮點型、布爾型等。

Tips : Go 語言中的每一個變量都有自己的類型,並且變量必須經過聲明才能開始使用。

變量聲明

答: Go 語言中的變量需要聲明後才能使用,同一作用域內不支持重複聲明。

Go 語言的變量聲明標準格式爲:var 變量名 變量類型

# 單一聲明: 變量聲明以關鍵字var開頭,變量類型放在變量的後面,行尾無需分號。
var name string
var age int
var isOk bool

# 批量聲明: 每聲明一個變量就需要寫var關鍵字會比較繁瑣,go語言中還支持批量變量聲明。
var (
  a string
  b int
  c bool
  d float32
)

變量初始化

Go 語言在聲明變量的時候,會自動對變量對應的內存區域進行初始化操作。每個變量會被初始化成其類型的默認值, 或者我們也可在聲明變量的時候爲其指定初始值。例如:

變量初始化的標準格式如下:var 變量名 類型 = 表達式

//# 單一變量初始化
var name string = "WeiyiGeek"
var age int = 18

//# 批量變量初始化
var name, age = "WeiyiGeek", 20

類型推導
描述: 有時候我們會將變量的類型省略,這個時候編譯器會根據等號右邊的值來推導變量的類型完成初始化。

var name = "WeiyiGeek"
var age = 18

短變量聲明
描述: 在函數內部可以使用更簡略的:=方式聲明並初始化變量。

func main() {
  count := 10
  username := "WeiyiGeek"
}

匿名變量

描述: 在使用多重賦值時,如果想要忽略某個值,可以使用匿名變量(anonymous variable)- 特殊變量。並且匿名變量不佔用命名空間,不會分配內存,所以匿名變量之間不存在重複聲明。 (在Lua等編程語言裏,匿名變量也被叫做啞元變量。)

匿名變量用一個下劃線(_)表示,例如:

func foo() (int, string) {
  return 10, "Q1mi"
}
func main() {
  x, _ := foo()
  _, y := foo()
  fmt.Println("x=", x)
  fmt.Println("y=", y)
}

示例演示:

package main

import "fmt"

// 變量聲明(單一-全局)
var singleName string
var notUseVar bool

// 變量聲明(批量-全局)
var (
  multiName string
  multiAge  int8
)

func main() {
  // 對聲明後的變量賦值
  singleName = "Weiyi_"
  multiName = "Geek"
  multiAge = 18

  // 變量初始化(局部)
  var name string = "WeiyiGeek"
  var sex, addr = "boy", "China"

  // 類型推導變量
  var flag = true
  var count = 1024

  // 簡短變量聲明(此種類型只能在函數中使用)
  briefCount := 65535

  fmt.Printf("My Name is %s, Sex is %s , Address: %s\n", name, sex, addr)
  fmt.Println("Alias Name :", singleName, multiName, " Age is :", multiAge)
  fmt.Print("類型推導 :", flag, count)
  fmt.Println(", 簡短變量 :", briefCount)
}

輸出結果:

API server listening at: 127.0.0.1:42954
My Name is WeiyiGeek, Sex is boy , Address: China
Alias Name : Weiyi_ Geek  Age is : 18
類型推導 :true 1024, 簡短變量 : 65535

Tips : Go 語言中變量必須先聲明後使用,而且聲明變量(非全局變量)後必須使用,如有不使用的變量編譯時報錯。

Tips :函數外的每個語句都必須以關鍵字開始 (var、const、func) 等

Tips : :=不能使用在函數外。

Tips : 匿名變量或者叫啞元變量(_)多用於佔位,表示忽略值,即當有些數據必須用變量接收但又不使用它時,可以採用_來接收改值。

Tips : 變量在同一個作用域中代碼塊({})中不能重複聲明同名的變量。

4. 常量 - Const

描述: 常量是在程序運行期間恆定不變的值,多用於定義程序運行期間不會改變的那些值。常量的聲明和變量聲明非常類似,只是把var換成了const,常量在定義的時候必須賦值。

常量聲明初始化格式:

// 單一聲明: 聲明瞭pi和e這兩個常量之後,在整個程序運行期間它們的值都不能再發生變化了。
const pi = 3.1415
const e = 2.7182

// 批量聲明
const (
  pi = 3.1415
  e = 2.7182
)

// 批量聲明(如果省略了值則表示和上面一行的值相同)
// 常量n1、n2、n3的值都是100。
const (
  n1 = 100
  n2
  n3
)

iota - 常量計數器

描述: iota 是 go 語言的常量計數器,只能在常量的表達式中使用。

Tips : iota 在 const 關鍵字出現時將被重置爲 0, const 中每新增一行常量聲明將使 iota 計數一次 (iota可理解爲const語句塊中的行索引)。

應用場景: 使用 iota 能簡化定義,在定義枚舉時很有用。

下面示例中幾個常見的 iota 示例:

const (
  n1 = iota //0
  n2        //1
  _
  n4        //3
)
const (
  n1 = iota //0
  n2 = 100  //100
  n3 = iota //2
  n4        //3
)
const n5 = iota //0
const (
  a, b = iota + 1, iota + 2 //1,2
  c, d                      //2,3
  e, f                      //3,4
)
const (
  _  = iota
  KB = 1 << (10 * iota)
  MB = 1 << (10 * iota)
  GB = 1 << (10 * iota)
  TB = 1 << (10 * iota)
  PB = 1 << (10 * iota)
)

示例演示:

package main

import "fmt"

// 單一常量聲明
const pi = 3.1415926535898

// 批量常量聲明
const (
  e    = 2.7182
  flag = false
)

// 特殊批量常量聲明
const (
  a = 1
  b
  _
  c
)

// iota 常量計數器
const (
  _     = iota               // 0
  d, e1 = iota + 1, iota + 2 // 2,3 常量名稱不能重複
  f, g  = iota + 1, iota + 2 // 3,4
)

const (
  _  = iota             // 0
  KB = 1 << (10 * iota) // 1024
  MB = 1 << (10 * iota)
  GB = 1 << (10 * iota)
  TB = 1 << (10 * iota)
  PB = 1 << (10 * iota)
)

func main() {
  fmt.Println("pi :", pi)
  fmt.Println("e :", e, " , flag:", false)
  fmt.Println("特殊批量常量聲明:", a, b, c)
  fmt.Println("iota 常量計數器 :", d, e1, f, g)
  fmt.Println("文件體積大小 :", KB, MB, GB, TB, PB)
}

執行結果:

pi : 3.1415926535898
e : 2.7182  , flag: false
特殊批量常量聲明: 1 1 1
iota 常量計數器 : 2 3 3 4
文件體積大小 : 1024 1048576 1073741824 1099511627776 1125899906842624

Tips : 常量聲明後不能在程序中進行重新賦值更改。

0x01 Go 語言基礎之基本數據類型

基本數據類型:
Go 語言中有豐富的數據類型,除了基本的整型、浮點型、布爾型、字符串外,還有數組、切片、結構體、函數、map、通道(channel)等。

Tips : Go 語言的基本類型和其他語言大同小異。

1. 整型

整型分爲以下兩個大類: 按長度分爲:int8、int16、int32、int64 對應的無符號整型:uint8、uint16、uint32、uint64

其中,uint8就是我們熟知的byte型,int16對應 C 語言中的short型,int64對應 C 語言中的long型。

486IBP

特殊整型

drQUpo

注意: 在使用int和 uint類型時,不能假定它是 32 位或 64 位的整型,而是考慮intuint可能在不同平臺上的差異。

注意事項: 獲取對象的長度的內建len()函數返回的長度可以根據不同平臺的字節長度進行變化。實際使用中,切片或 map 的元素數量等都可以用int來表示。在涉及到二進制傳輸、讀寫文件的結構描述時,爲了保持文件的結構不會受到不同編譯目標平臺字節長度的影響,不要使用int和 uint

數字字面量語法(Number literals syntax)

Go1.13 版本之後引入了數字字面量語法,這樣便於開發者以二進制、八進制或十六進制浮點數的格式定義數字,例如:

而且還允許我們用 _ 來分隔數字,比如說: v := 123_456 表示 v 的值等於 123456。

簡單示例: 我們可以藉助 fmt 函數來將一個整數以不同進制形式展示。

package main
 
import "fmt"
 
func main(){
  // 十進制以不同的進制展示
  var a int = 10
  fmt.Printf("%b \n", a)   // 1010  佔位符%b表示二進制
  fmt.Printf("%o \n", a)   // 12    佔位符%o表示八進制
  fmt.Printf("%d \n", a)   // 10    佔位符%d表示十進制
  fmt.Printf("0x%x \n", a) // 0xa  佔位符%x表示十六進制

  // 八進制(以0開頭)
  var b int = 077
  fmt.Printf("%b \n", b)   // 111111
  fmt.Printf("%o \n", b)   // 77
  fmt.Printf("%d \n", b)   // 63
  fmt.Printf("0x%x \n", b) // 0x3f

  // 十六進制(以0x開頭)
  var c int = 0xff
  fmt.Printf("0x%x \n", c) // 0xff
  fmt.Printf("0X%X \n", c) // 0xFF

  // 數字字面量語法(Number literals syntax)
  binary := 0b1111
  octal := 0o17
  digital := 15
  hexadecimal := 0xf
  specialhexa := 0x8p-2    // 8 / 2^2 = 2
  underline := 10_24

  fmt.Printf("binary : %b , digital : %d\n", binary, binary)
  fmt.Printf("octal : %o , digital : %d\n", octal, octal)
  fmt.Printf("digital type (變量類型): %T,digital : %d\n", digital, digital)
  fmt.Printf("hexadecimal : %x, digital : %d, specialhexa : %f\n", hexadecimal, hexadecimal, specialhexa)
  fmt.Printf("underline : %d \n", underline)
}

輸出結果:

# Int 整型示例:
1010 
12 
10 
0xa 
111111 
77 
63 
0x3f 
0xff 
0XFF 
binary : 1111 , digital : 15
octal : 17 , digital : 15
digital type (變量類型): int,digital : 15

2. 浮點型

Go 語言支持兩種浮點型數:float32float64
這兩種浮點型數據格式遵循IEEE 754標準:

簡單示例: 打印浮點數時,可以使用fmt包配合動詞%f,代碼如下:

package main
import (
    "fmt"
    "math"
)
func main() {
    var floatnumber float64 = 1024.00
  fmt.Printf("數據類型: %T , floatnumber: %.1f\n", floatnumber, floatnumber)
  fmt.Printf("%f,%.2f\n", math.Pi, math.Pi) // 保留小數點後兩位
  fmt.Printf("float32的浮點數的最大範圍 :%d ~ %f\n", 0, math.MaxFloat32)
  fmt.Printf("float64的浮點數的最大範圍 :%d ~ %f\n", 0, math.MaxFloat64)
}

執行結果:

數據類型: float64 , floatnumber: 1024.0
3.141593,3.14
float32的浮點數的最大範圍 :0 ~ 340282346638528859811704183484516925440.000000
float64的浮點數的最大範圍 :0 ~ 179769313486231570814527423731704356798070567525844996598917476803157260780028538760589558632766878171540458953514382464234321326889464182768467546703537516986049910576551282076245490090389328944075868508455133942304583236903222948165808559332123348274797826204144723168738177180919299881250404026184124858368.000000

3. 複數

描述: 複數有實部和虛部,complex64 的實部和虛部爲 32 位,complex128 的實部和虛部爲 64 位。

例如: complex64 和 complex128 類型變量輸出示例

var c1 complex64
c1 = 1 + 2i
var c2 complex128
c2 = 2 + 3i
fmt.Println(c1) // (1+2i)
fmt.Println(c2) // (2+3i)

4. 布爾值

Go 語言中以bool類型進行聲明布爾型數據,布爾型數據只有true(真)false(假)兩個值。

fmt.Println("# 4.布爾型示例:")
var flag bool = true
fmt.Printf("數據類型: %T ,任意類型輸出: %v", flag, flag)  // 數據類型: bool ,任意類型輸出: true

注意:

  1. 布爾類型變量的默認值爲false

  2. Go 語言中不允許將整型強制轉換爲布爾型.

  3. 布爾型無法參與數值運算,也無法與其他類型進行轉換。

5. 字符串

描述: Go 語言中的字符串以原生數據類型出現,使用字符串就像使用其他原生數據類型(int、bool、float32、float64 等)一樣。
Go 語言裏的字符串的內部實現使用UTF-8編碼。 字符串的值爲雙引號(")中的內容,可以在 Go 語言的源碼中直接添加非 ASCII 碼字符,例如:

s1 := "hello"
s2 := "你好"
c1 := 'g'
c2 := 'o'

Tips : Go 語言中用雙引號包裹的是字符串,而單引號包裹的是字符

字符串轉義符

Go 語言的字符串常見轉義符包含回車、換行、單雙引號、製表符等,如下表所示。

fKyKj7

舉個例子,我們要打印一個 Windows 平臺下的一個文件路徑:

package main
import (
    "fmt"
)
func main() {
  s1 := "'c:\\weiyigeek\\go\\hello'"
    fmt.Println("str :=",s1)
    fmt.Println("str := \"c:\\Code\\weiyigeek\\go.exe\"")
}

多行字符串

Go 語言中要定義一個多行字符串時,就必須使用反引號字符:

s1 := `第一行
第二行
第三行
`
s2 := `c:\weiyigeek\go\hello`  // 注意點此處沒用轉義符(\) 也能輸出路徑

fmt.Println(s1,s2)

Tips: 反引號間換行將被作爲字符串中的換行,但是所有的轉義字符均無效,文本將會原樣輸出。

字符串的常用操作

m3VPkd

示例演示:

// 字符串型示例
func stringdemo() {
  // 字符
  c1 := 'a'
  c2 := 'A'

  // 字符串 (單行與多行以及轉義)
  s1 := "Name"
  s2 := "姓名"
  s3 := `
  這是一個 
        多行字符串案例!
  This is mutlilineString Example!
  Let's Go   // 特點:原樣輸出
  `
  // 轉義演示
  s4 := "'c:\\weiyigeek\\go\\hello'"
  s5 := `c:\weiyigeek\go\hello`

  fmt.Printf("c1 char : %c,\t c2 char %c -> digital : %d\n", c1, c2, c2)
  fmt.Println(s1, s2)
  fmt.Println(s3)
  fmt.Println(s4, s5)

  // 字符串常用函數
  fmt.Println("s1 String length:", len(s1), "s2 string length:", len(s2))

  info := fmt.Sprintf("%s (%s): %s", s1, s2, "WeiyiGeek")
  fmt.Println("Infomation : "+"個人信息", info)

  fmt.Println("字符串分割 :", strings.Split(s5, "\\"))

  fmt.Println("判斷字符串是否包含go", strings.Contains(s3, "go"))

  fmt.Println(strings.HasPrefix(s1, "N"), strings.HasSuffix(s1, "e"))

  fmt.Println(strings.Index(s4, "weiyigeek"), strings.LastIndex(s4, "weiyigeek"))

  s6 := strings.Split(s5, "\\")
  fmt.Println("字符串間隔符 : ", strings.Join(s6, "-"))
}

執行效果:

# 5.字符串型示例:
c1 char : a,   c2 char A -> digital : 65
Name 姓名

  這是一個 
        多行字符串案例!
  This is mutlilineString Example!
  Let's Go   // 特點:原樣輸出
  
'c:\weiyigeek\go\hello' c:\weiyigeek\go\hello
s1 String length: 4 s2 string length: 6
Infomation : 個人信息 Name (姓名): WeiyiGeek
字符串分割 : [c: weiyigeek go hello]
判斷字符串是否包含go false
true true
4 4
字符串間隔符 :  c:-weiyigeek-go-hello

6.byte 和 rune 類型

描述: 組成每個字符串的元素叫做 “字符”,可以通過遍歷或者單個獲取字符串元素獲得字符。 字符用單引號(’)包裹起來,如:

var a = '中'
var b = 'x'
c := 'a'

Go 語言的字符有以下兩種:

  1. uint8類型,或者叫 byte 型,代表了ASCII碼的一個字符(1B)。

  2. rune類型,代表一個 UTF-8字符, 並且一個 rune 字符由一個或多個 byte 組成(3B~4B)。

Tips : 當需要處理中文、日文或者其他複合字符時,則需要用到rune類型。rune類型實際是一個int32

Go 使用了特殊的 rune 類型來處理 Unicode,讓基於 Unicode 的文本處理更爲方便,也可以使用 byte 型進行默認字符串處理,性能和擴展性都有照顧。

簡單示例:

// 遍歷字符串
func traversalString() {
  s := "hello沙河"
   // byte 類型
  for i := 0; i < len(s); i++ {
    fmt.Printf("%v(%c) ", s[i], s[i])
  }
  fmt.Println()
  // rune 類型
  for _, r := range s { 
    fmt.Printf("%v(%c) ", r, r)
  }
  fmt.Println()
}

結果輸出:

// byte 類型 (中文亂碼)
104(h) 101(e) 108(l) 108(l) 111(o) 230(æ) 178(²) 153() 230(æ) 178(²) 179(³) 

// rune 類型 (中文正常)
104(h) 101(e) 108(l) 108(l) 111(o) 27801() 27827()

Q: 爲什麼出現上述情況?

答: 因爲 UTF8 編碼下一箇中文漢字由3~4個字節(4*8bit)組成,所以我們不能簡單的按照字節去遍歷一個包含中文的字符串,否則就會出現上面輸出中第一行的結果。

Tips : 字符串底層是一個 byte 數組,所以可以和[]byte類型相互轉換。字符串是不能修改的字符串是由 byte 字節組成,所以字符串的長度是 byte 字節的長度。

7. 類型轉換

描述:Go 語言中只有強制類型轉換,沒有隱式類型轉換。該語法只能在兩個類型之間支持相互轉換的時候使用。

強制類型轉換的基本語法如下:

T(表達式)  # 其中,T表示要轉換的類型。表達式包括變量、複雜算子和函數返回值等.

Tips : Boolen 類型不能強制轉換爲整型。

整型轉浮點型

比如:計算直角三角形的斜邊長時使用 math 包的 Sqrt() 函數,該函數接收的是 float64 類型的參數,而變量 a 和 b 都是 int 類型的,這個時候就需要將 a 和 b 強制類型轉換爲 float64 類型。

func sqrtDemo() {
  var a, b = 3, 4
  var c int
  // math.Sqrt() 接收的參數是float64類型,需要強制轉換
  c = int(math.Sqrt(float64(a*a + b*b)))
  fmt.Println(c)
}

Tips : 在 Go 語言中不同類型的值不能直接賦值,例如 float32 類型變量 a 的值不能直接賦值給 floa64 類型變量 b 的值。

字符串類型轉換

描述: 如果修改字符串,需要先將其轉換成[]rune[]byte,完成後再轉換爲string。無論哪種轉換,都會重新分配內存,並複製字節數組

func changeString() {
  s1 := "big"
  // 強制類型轉換
  byteS1 := []byte(s1)
  byteS1[0] = 'p'
  fmt.Println(string(byteS1))

  s2 := "白蘿蔔"
  runeS2 := []rune(s2)
  runeS2[0] = '紅'
  fmt.Println(string(runeS2))
}

Tips : 在一個字符串中如果既有中文也存在英文,我們則可以使用byte[]類型(1B) 來存放 ASCII 碼錶示的字符 (0~255),如果是中文則使用rune[](4B) 類型來存放或者週轉。

案例演示:

// Byte與Rune類型示例
func brdemo() {
  var c1 = 'a' // int32 類型
  var c2 = 'A' // int32 類型
  z1 := '中'    // int32 類型
  z2 := '文'    // int32 類型
  z3 := "中"    // string 類型 (雙引號)

  // 字符不同格式輸出
  fmt.Printf("字符 :%d (%c) , %d (%c) \n", c1, c1, c2, c2)
  fmt.Printf("中文字符 :%d (%v) = %c , %d (%v) = %c \n", z1, z1, z1, z2, z2, z2)
  fmt.Printf("單雙引號不同類型 : c1 = %c (%T) , z2 = %c (%T) ,  z3 = %s (%T) \n", c1, c1, z2, z2, z3, z3)

  // 中英文字符串修改
  s1 := "a和我都愛中國"
  s2 := "爲 Hello 中國 World,Go 語言 學習"

  // 將字符類型轉化爲byte類型
  c3 := byte(c2)
  fmt.Printf("強制轉化類型 : c2 = %c (%T) , byte(c2) = %c (%T) \n", c2, c2, c3, c3)

  // 將字符串類型轉化爲string類型
  r1 := []rune(s1) // 強制轉化字符串爲一個rune切片
  r1[0] = '您'      // 注意此處需傳入爲字符
  fmt.Println("修改後中文字符串輸出(未類型轉換):", r1)
  fmt.Println("修改後中文字符串輸出(已類型轉換):", s1, string(r1)) // 強制轉化rune切片爲字符串

  // 將整型轉化成爲浮點數類型
  // 計算直角三角形的斜邊長
  var a, b = 3, 4
  var c int = int(math.Sqrt(float64(a*a + b*b)))
  fmt.Println("計算直角三角形的斜邊長 (a=3,b=4) c =", c)

  // 統計字符串中中文個數
  res := []rune(s2)
  reslen := len(res)
  count := 0
  for i := 0; i < reslen; i++ {
    if res[i] > 255 {
      count++
    }
  }
  fmt.Printf("字符串:%s (Length = %d),一共有 %d 箇中文字符", s2, reslen, count)
}

執行結果:

# 6.Byte與Rune型示例:
字符 :97 (a) , 65 (A) 
中文字符 :20013 (20013) = 中 , 25991 (25991) = 文 
單雙引號不同類型 : c1 = a (int32) , z2 =(int32) ,  z3 =(string) 
強制轉化類型 : c2 = A (int32) , byte(c2) = A (uint8) 
修改後中文字符串輸出(未類型轉換)[24744 21644 25105 37117 29233 20013 22269]
修改後中文字符串輸出(已類型轉換): a和我都愛中國 您和我都愛中國
計算直角三角形的斜邊長 (a=3,b=4) c = 5
字符串:爲 Hello 中國 World,Go 語言 學習 (Length = 25),一共有 7 箇中文字符

0x02 Go 語言基礎之運算符

什麼是運算符?

答: 運算符用於在程序運行時執行數學或邏輯運算。

1. 分類說明

描述: 與其他編程語言類似 Go 語言內置的運算符有如下幾種

  1. 算術運算符

  2. 關係運算符

  3. 邏輯運算符

  4. 位運算符

  5. 賦值運算符

2. 算術運算符

oOK3L4

注意: ++(自增)和--(自減)在 Go 語言中是單獨的語句,並不是運算符。

3. 關係運算符

p2XIXk

Tips : Go 語言是強類型的所以必須相同類型變量才能進行比較。

4. 邏輯運算符

cmPCmH

5. 位運算符

描述: 位運算符對整數在內存中的二進制位進行操作。

oTAKFr

6. 賦值運算符

xyshfc

簡單示例:

a += 1  // a = a + 1
a %= 3  // a = a % 3
a <<= 4 // a = a << 4 
a ^= 5  // a = a ^ 5

0x03 Go 語言基礎之流程控制

Q: 什麼是流程控制

流程控制是每種編程語言控制邏輯走向和執行次序的重要部分,流程控制可以說是一門語言的 “經脈”。

Tips : Go 語言中最常用的流程控制有iffor,而switchgoto主要是爲了簡化代碼、降低重複代碼而生的結構,屬於擴展類的流程控制。

1.if else(分支結構)

if 條件判斷基本寫法

描述: Go 語言中if條件判斷的格式如下:

if 表達式1 {
  分支1
} else if 表達式2 {
  分支2
} else{
  分支3
}

當表達式 1 的結果爲true時,執行分支 1,否則判斷表達式 2,如果滿足則執行分支 2,都不滿足時,則執行分支 3。 if 判斷中的else ifelse都是可選的,可以根據實際需要進行選擇。

Go 語言規定與if匹配的左括號{必須與if和表達式放在同一行,{放在其他位置會觸發編譯錯誤。 同理,與else匹配的{也必須與else寫在同一行,else也必須與上一個ifelse if右邊的大括號在同一行。

示例 1:

func ifDemo1() {
  score := 65
  if score >= 90 {
    fmt.Println("A")
  } else if score > 75 {
    fmt.Println("B")
  } else {
    fmt.Println("C") // 輸出結果
  }
}

if 條件判斷特殊寫法

描述: if 條件判斷還有一種特殊的寫法,可以在 if 表達式之前添加一個執行語句,再根據變量值進行判斷,舉個例子:

示例 2:

func ifDemo2() {
  score := 88 // 注意變量作用域的影響
  if score := 65; score >= 90 {
    fmt.Println("A", score)
  } else if score > 75 {
    fmt.Println("B", score)
  } else {
    fmt.Println("C", score) // 輸出結果
  }
  fmt.Println("score : ", score)
}

執行結果:

C 65
score :  88

思考題: 上下兩種寫法的區別在哪裏?

答: 上述 示例 1 與 示例 2 區別在於定義 score 變量位置得不同, 示例 1 中的 score 變量在函數代碼塊中有效, 而示例 2 中的 score 變量作用域只在if...else代碼塊中有效。

2.for(循環結構)

描述: Go 語言中的所有循環類型均可以使用for關鍵字來完成。

for 循環的基本格式如下:

for 初始語句;條件表達式;結束語句{
   循環體語句
}

條件表達式返回true時循環體不停地進行循環,直到條件表達式返回false時自動退出循環。

func forDemo() {
  for i := 0; i < 10; i++ {
    fmt.Println(i)
  }
}

for 循環的初始語句可以被忽略,但是初始語句後的分號必須要寫,例如:

func forDemo2() {
  i := 0
  for ; i < 10; i++ {
    fmt.Println(i)
  }
}

for 循環的初始語句和結束語句都可以省略,例如:

func forDemo3() {
  i := 0
  for i < 10 {
    fmt.Println(i)
    i++
  }
}

for 無限循環,這種寫法類似於其他編程語言中的while,在while後添加一個條件表達式,滿足條件表達式時持續循環,否則結束循環。
例如: for 循環可以通過breakgotoreturnpanic語句強制退出循環。

for {
  循環體語句
}

3.for range(鍵值循環)

描述: Go 語言中可以使用for range遍歷數組、切片、字符串、map 及通道(channel)。

通過for range遍歷的返回值有以下規律:

  1. 數組、切片、字符串返回索引和值。

  2. map 返回鍵和值。

  3. 通道(channel)只返回通道內的值。

簡單示例:

s1 := "Hello,Go 輸出的是中文"
for i, v := range s1 {
  fmt.Printf("Index : %d ,Value : %s , Number : %v \n", i, string(v), v)
}

輸出結果:

Index : 0 ,Value : H , Number : 72 
Index : 1 ,Value : e , Number : 101 
Index : 2 ,Value : l , Number : 108 
Index : 3 ,Value : l , Number : 108 
Index : 4 ,Value : o , Number : 111 
Index : 5 ,Value : , , Number : 44 
Index : 6 ,Value : G , Number : 71 
Index : 7 ,Value : o , Number : 111 
Index : 8 ,Value :   , Number : 32 
Index : 9 ,Value : 輸 , Number : 36755 
Index : 12 ,Value : 出 , Number : 20986 
Index : 15 ,Value : 的 , Number : 30340 
Index : 18 ,Value : 是 , Number : 26159 
Index : 21 ,Value : 中 , Number : 20013 
Index : 24 ,Value : 文 , Number : 25991

4.switch case(選擇語句)

描述: 使用switch語句可方便地對大量的值進行條件判斷,即簡化大量判斷。

func switchDemo1() {
  finger := 3
  switch finger {
  case 1:
    fmt.Println("大拇指")
  case 2:
    fmt.Println("食指")
  case 3:
    fmt.Println("中指")
  case 4:
    fmt.Println("無名指")
  case 5:
    fmt.Println("小拇指")
  default:
    fmt.Println("無效的輸入!")
  }
}

Go 語言規定每個switch只能有一個default分支, 但一個分支可以有多個值,多個 case 值中間使用英文逗號分隔。

func testSwitch3() {
  switch n := 7; n {
  case 1, 3, 5, 7, 9:
    fmt.Println("奇數")
  case 2, 4, 6, 8:
    fmt.Println("偶數")
  default:
    fmt.Println(n)
  }
}

分支還可以使用表達式,這時候 switch 語句後面不需要再跟判斷變量。例如:

func switchDemo4() {
  age := 30
  switch {
  case age < 25:
    fmt.Println("好好學習吧")
  case age > 25 && age < 35:
    fmt.Println("好好工作吧")
  case age > 60:
    fmt.Println("好好享受吧")
  default:
    fmt.Println("活着真好")
  }
}

fallthrough語法: 可以執行滿足條件的 case 的下一個 case,是爲了兼容 C 語言中的 case 設計的(值得學習)。

func switchDemo5() {
  s := "a"
  switch {
  case s == "a":
    fmt.Println("a")
    fallthrough
  case s == "b":
    fmt.Println("b")
  case s == "c":
    fmt.Println("c")
  default:
    fmt.Println("...")
  }
}

結果輸出:

a
b

5.goto(跳轉到指定標籤)

描述: goto語句通過標籤進行代碼間的無條件跳轉。goto語句可以在快速跳出循環、避免重複退出上有一定的幫助, Go 語言中使用goto語句能簡化一些代碼的實現過程。

例如: 雙層嵌套的 for 循環要退出時

func gotoDemo1() {
  var breakFlag bool
  for i := 0; i < 10; i++ {
    for j := 0; j < 10; j++ {
      if j == 2 {
        // 設置退出標籤
        breakFlag = true
        break
      }
      fmt.Printf("%v-%v\n", i, j)
    }
    // 外層for循環判斷
    if breakFlag {
      break
    }
  }
}

使用goto語句能簡化代碼:

func gotoDemo2() {
  for i := 0; i < 10; i++ {
    for j := 0; j < 10; j++ {
      if j == 2 {
        // 設置退出標籤
        goto breakTag
      }
      fmt.Printf("%v-%v\n", i, j)
    }
  }
  return
  // 標籤
  breakTag:
    fmt.Println("正結束for循環")
    fmt.Println("已結束for循環")
}

執行結果:

0-0
0-1
正結束for循環
已結束for循環

6.break(跳出循環)

描述: break語句可以結束forswitchselect的代碼塊。

break語句還可以在語句後面添加標籤,表示退出某個標籤對應的代碼塊,標籤要求必須定義在對應的forswitch和 select的代碼塊上。 舉個例子:

func breakDemo1() {
BREAKDEMO1:
  for i := 0; i < 10; i++ {
    for j := 0; j < 10; j++ {
      if j == 2 {
        break BREAKDEMO1
      }
      fmt.Printf("%v-%v\n", i, j)
    }
  }
  fmt.Println("...")
}

7.continue(繼續下次循環)

描述: continue語句可以結束當前循環,開始下一次的循環迭代過程,僅限在for循環內使用。

在 continue語句後添加標籤時,表示開始標籤對應的循環。例如:

func continueDemo() {
forloop1:
  for i := 0; i < 5; i++ {
    // forloop2:
    for j := 0; j < 5; j++ {
      if i == 2 && j == 2 {
        continue forloop1
      }
      fmt.Printf("%v-%v\n", i, j)
    }
  }
}

歡迎關注 “WeiyiGeek” 公衆號

本文由 Readfog 進行 AMP 轉碼,版權歸原作者所有。
來源https://mp.weixin.qq.com/s/MS_lSaA72Qk5nyJjDhY1mg