Go變量
變量是用於保存一個值的存儲位置,允許其存儲的值在運行時動態的變化。每聲明一個變量,都會為其分配一塊內存以存儲對應類型的值,前往參考手冊-變量以查看更多細節。
聲明
在 go 中的類型聲明是後置的,變量的聲明會用到var關鍵字,格式為var 變量名 類型名,變量名的命名規則必須遵守標識符的命名規則。
var intNum int
var str string
var char byte當要聲明多個相同類型的變量時,可以只寫一次類型
var numA, numB, numC int當要聲明多個不同類型的變量時,可以使用()進行包裹,可以存在多個()。
var (
name string
age int
address string
)
var (
school string
class int
)一個變量如果只是聲明而不賦值,那麼變量存儲的值就是對應類型的零值。
賦值
賦值會用到運算符=,例如
var name string
name = "jack"也可以聲明的時候直接賦值
var name string = "jack"或者這樣也可以
var name string
var age int
name, age = "jack", 1第二種方式每次都要指定類型,可以使用官方提供的語法糖:短變量初始化,可以省略掉var關鍵字和後置類型,具體是什麼類型交給編譯器自行推斷。
name := "jack" // 字符串類型的變量。雖然可以不用指定類型,但是在後續賦值時,類型必須保持一致,下面這種代碼無法通過編譯。
a := 1
a = "1"還需要注意的是,短變量初始化不能使用nil,因為nil不屬於任何類型,編譯器無法推斷其類型。
name := nil // 無法通過編譯短變量聲明可以批量初始化
name, age := "jack", 1短變量聲明方式無法對一個已存在的變量使用,比如
// 錯誤示例
var a int
a := 1
// 錯誤示例
a := 1
a := 2但是有一種情況除外,那就是在賦值舊變量的同時聲明一個新的變量,比如
a := 1
a, b := 2, 2這種代碼是可以通過編譯的,變量a被重新賦值,而b是新聲明的。
在 go 語言中,有一個規則,那就是所有在函數中的變量都必須要被使用,比如下面的代碼只是聲明了變量,但沒有使用它
func main() {
a := 1
}那麼在編譯時就會報錯,提示你這個變量聲明了但沒有使用
a declared and not used這個規則僅適用於函數內的變量,對於函數外的包級變量則沒有這個限制,下面這個代碼就可以通過編譯。
var a = 1
func main() {
}匿名
用下劃線可以表示不需要某一個變量
Open(name string) (*File, error)比如os.Open函數有兩個返回值,我們只想要第一個,不想要第二個,可以按照下面這樣寫
file, _ := os.Open("readme.txt")未使用的變量是無法通過編譯的,當你不需要某一個變量時,就可以使用下劃線_代替。
交換
在 Go 中,如果想要交換兩個變量的值,不需要使用指針,可以使用賦值運算符直接進行交換,語法上看起來非常直觀,例子如下
num1, num2 := 25, 36
num1, num2 = num2, num1三個變量也是同樣如此
num1, num2, num3 := 25, 36, 49
num1, num2, num3 = num3, num2, num1思考下面這一段代碼,這是計算斐波那契數列的一小段代碼,三個變量在計算後的值分別是什麼
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 在進行多個變量賦值運算時,它的順序是先計算值再賦值,並非從左到右計算。
a, b, c = b, c, a+b你可能會以為它會被展開成下面這段
a = b
b = c
c = a + b但實際上它會將 a, b, c 三個數的值分別計算好再賦給它們,就等同於下面這段代碼
a, b, c = 1, 1, 0+1當涉及到函數調用時,這個效果就更為明顯,我們有一個函數sum可以計算兩個數字的返回值
func sum(a, b int) int {
return a + b
}通過函數來進行兩數相加
a, b, c := 0, 1, 1
a, b, c = b, c, sum(a, b)結果沒有變化,在計算sum函數返回值時,它的入參依舊是 0 和 1
1 1 1所以代碼應該這樣分開寫。
a, b = b, c
c = a + b比較
變量之間的比較有一個大前提,那就是它們之間的類型必須相同,go 語言中不存在隱式類型轉換,像下面這樣的代碼是無法通過編譯的
func main() {
var a uint64
var b int64
fmt.Println(a == b)
}編譯器會告訴你兩者之間類型並不相同
invalid operation: a == b (mismatched types uint64 and int64)所以必須使用強制類型轉換
func main() {
var a uint64
var b int64
fmt.Println(int64(a) == b)
}在沒有泛型之前,早期 go 提供的內置min,max函數只支持浮點數,到了 1.21 版本,go 才終於將這兩個內置函數用泛型重寫,現在可以使用min函數比較最小值
minVal := min(1, 2, -1, 1.2)使用max函數比較最大值
maxVal := max(100, 22, -1, 1.12)它們的參數支持所有的可比較類型,go 中的可比較類型有
- 布爾
- 數字
- 字符串
- 指針
- 通道 (僅支持判斷是否相等)
- 元素是可比較類型的數組(切片不可比較)(僅支持判斷是否相等)(僅支持相同長度的數組間的比較,因為數組長度也是類型的一部分,而不同類型不可比較)
- 字段類型都是可比較類型的結構體(僅支持判斷是否相等)
除此之外,還可以通過導入標准庫cmp來判斷,不過僅支持有序類型的參數,在 go 中內置的有序類型只有數字和字符串。
import "cmp"
func main() {
cmp.Compare(1, 2)
cmp.Less(1, 2)
}代碼塊
在函數內部,可以通過花括號建立一個代碼塊,代碼塊彼此之間的變量作用域是相互獨立的。例如下面的代碼
func main() {
a := 1
{
a := 2
fmt.Println(a)
}
{
a := 3
fmt.Println(a)
}
fmt.Println(a)
}它的輸出是
2
3
1塊與塊之間的變量相互獨立,不受干擾,無法訪問,但是會受到父塊中的影響。
func main() {
a := 1
{
a := 2
fmt.Println(a)
}
{
fmt.Println(a)
}
fmt.Println(a)
}它的輸出是
2
1
1