Skip to content

Go變量

變量是用於保存一個值的存儲位置,允許其存儲的值在運行時動態的變化。每聲明一個變量,都會為其分配一塊內存以存儲對應類型的值,前往參考手冊-變量以查看更多細節。

聲明

在 go 中的類型聲明是後置的,變量的聲明會用到var關鍵字,格式為var 變量名 類型名,變量名的命名規則必須遵守標識符的命名規則。

go
var intNum int
var str string
var char byte

當要聲明多個相同類型的變量時,可以只寫一次類型

go
var numA, numB, numC int

當要聲明多個不同類型的變量時,可以使用()進行包裹,可以存在多個()

go
var (
  name    string
  age     int
  address string
)

var (
  school string
  class int
)

一個變量如果只是聲明而不賦值,那麼變量存儲的值就是對應類型的零值。

賦值

賦值會用到運算符=,例如

go
var name string
name = "jack"

也可以聲明的時候直接賦值

go
var name string = "jack"

或者這樣也可以

go
var name string
var age int
name, age = "jack", 1

第二種方式每次都要指定類型,可以使用官方提供的語法糖:短變量初始化,可以省略掉var關鍵字和後置類型,具體是什麼類型交給編譯器自行推斷。

go
name := "jack" // 字符串類型的變量。

雖然可以不用指定類型,但是在後續賦值時,類型必須保持一致,下面這種代碼無法通過編譯。

a := 1
a = "1"

還需要注意的是,短變量初始化不能使用nil,因為nil不屬於任何類型,編譯器無法推斷其類型。

go
name := nil // 無法通過編譯

短變量聲明可以批量初始化

go
name, age := "jack", 1

短變量聲明方式無法對一個已存在的變量使用,比如

go
// 錯誤示例
var a int
a := 1

// 錯誤示例
a := 1
a := 2

但是有一種情況除外,那就是在賦值舊變量的同時聲明一個新的變量,比如

go
a := 1
a, b := 2, 2

這種代碼是可以通過編譯的,變量a被重新賦值,而b是新聲明的。

在 go 語言中,有一個規則,那就是所有在函數中的變量都必須要被使用,比如下面的代碼只是聲明了變量,但沒有使用它

go
func main() {
  a := 1
}

那麼在編譯時就會報錯,提示你這個變量聲明了但沒有使用

a declared and not used

這個規則僅適用於函數內的變量,對於函數外的包級變量則沒有這個限制,下面這個代碼就可以通過編譯。

go
var a = 1

func main() {

}

匿名

用下劃線可以表示不需要某一個變量

go
Open(name string) (*File, error)

比如os.Open函數有兩個返回值,我們只想要第一個,不想要第二個,可以按照下面這樣寫

go
file, _ := os.Open("readme.txt")

未使用的變量是無法通過編譯的,當你不需要某一個變量時,就可以使用下劃線_代替。

交換

在 Go 中,如果想要交換兩個變量的值,不需要使用指針,可以使用賦值運算符直接進行交換,語法上看起來非常直觀,例子如下

go
num1, num2 := 25, 36
num1, num2 = num2, num1

三個變量也是同樣如此

go
num1, num2, num3 := 25, 36, 49
num1, num2, num3  = num3, num2, num1

思考下面這一段代碼,這是計算斐波那契數列的一小段代碼,三個變量在計算後的值分別是什麼

go
a, b, c := 0, 1, 1
a, b, c = b, c, a+b

答案是

1 1 1

你可能會疑惑為什麼不是

1 1 2

明明 a 已經被賦予 b 的值了,為什麼 a+b 的結果還是 1?go 在進行多個變量賦值運算時,它的順序是先計算值再賦值,並非從左到右計算。

go
a, b, c = b, c, a+b

你可能會以為它會被展開成下面這段

go
a = b
b = c
c = a + b

但實際上它會將 a, b, c 三個數的值分別計算好再賦給它們,就等同於下面這段代碼

go
a, b, c = 1, 1, 0+1

當涉及到函數調用時,這個效果就更為明顯,我們有一個函數sum可以計算兩個數字的返回值

go
func sum(a, b int) int {
  return a + b
}

通過函數來進行兩數相加

go
a, b, c := 0, 1, 1
a, b, c = b, c, sum(a, b)

結果沒有變化,在計算sum函數返回值時,它的入參依舊是 0 和 1

1 1 1

所以代碼應該這樣分開寫。

go
a, b = b, c
c = a + b

比較

變量之間的比較有一個大前提,那就是它們之間的類型必須相同,go 語言中不存在隱式類型轉換,像下面這樣的代碼是無法通過編譯的

go
func main() {
  var a uint64
  var b int64
  fmt.Println(a == b)
}

編譯器會告訴你兩者之間類型並不相同

invalid operation: a == b (mismatched types uint64 and int64)

所以必須使用強制類型轉換

go
func main() {
  var a uint64
  var b int64
  fmt.Println(int64(a) == b)
}

在沒有泛型之前,早期 go 提供的內置minmax函數只支持浮點數,到了 1.21 版本,go 才終於將這兩個內置函數用泛型重寫,現在可以使用min函數比較最小值

go
minVal := min(1, 2, -1, 1.2)

使用max函數比較最大值

go
maxVal := max(100, 22, -1, 1.12)

它們的參數支持所有的可比較類型,go 中的可比較類型有

  • 布爾
  • 數字
  • 字符串
  • 指針
  • 通道 (僅支持判斷是否相等)
  • 元素是可比較類型的數組(切片不可比較)(僅支持判斷是否相等)(僅支持相同長度的數組間的比較,因為數組長度也是類型的一部分,而不同類型不可比較)
  • 字段類型都是可比較類型的結構體(僅支持判斷是否相等)

除此之外,還可以通過導入標准庫cmp來判斷,不過僅支持有序類型的參數,在 go 中內置的有序類型只有數字和字符串。

go
import "cmp"

func main() {
  cmp.Compare(1, 2)
  cmp.Less(1, 2)
}

代碼塊

在函數內部,可以通過花括號建立一個代碼塊,代碼塊彼此之間的變量作用域是相互獨立的。例如下面的代碼

go
func main() {
  a := 1

  {
    a := 2
    fmt.Println(a)
  }

  {
    a := 3
    fmt.Println(a)
  }
  fmt.Println(a)
}

它的輸出是

2
3
1

塊與塊之間的變量相互獨立,不受干擾,無法訪問,但是會受到父塊中的影響。

go
func main() {
  a := 1

  {
    a := 2
    fmt.Println(a)
  }

  {
    fmt.Println(a)
  }
  fmt.Println(a)
}

它的輸出是

2
1
1

Golang學習網由www.golangdev.cn整理維護