1.1 變量
Go 是靜态類型語⾔,不能在運⾏期改變變量類型。
使⽤關鍵字 var 定義變量,⾃動初始化為零值。如果提供初始化值,可省略變量類型,由
編譯器⾃動推斷。
var x int
var f float32 = 1.6
var s = "abc"
在函數内部,可⽤更簡略的 ":=" ⽅式定義變量。
func main() {
x := 123 // 注意檢查,是定義新局部變量,還是修改全局變量。該⽅式容易造成錯誤。
}
可⼀次定義多個變量。
var x, y, z int
var s, n = "abc", 123
var (
a int
b float32
)
func main() {
n, s := 0x1234, "Hello, World!"
println(x, s, n)
}
多變量指派時,先計算所有相關值,然後再從左到右依次指派。
data, i := [3]int{0, 1, 2}, 0
i, data[i] = 2, 100 // (i = 0) -> (i = 2), (data[0] = 100)
特殊隻寫變量 "_",⽤于忽略值占位。
func test() (int, string) {
return 1, "abc"
}
func main() {
_, s := test()
println(s)
}
編譯器會将未使⽤的局部變量當做錯誤。
var s string // 全局變量沒問題。
func main() {
i := 0 // Error: i declared and not used。(可使⽤ "_ = i" 規避)
}
注意重新指派與定義新同名變量的差別。
s := "abc"
println(&s)
s, y := "hello", 20 // 重新指派: 與前 s 在同⼀層次的代碼塊中,且有新的變量被定義。
println(&s, y) // 通常函數多傳回值 err 會被重複使⽤。
{
s, z := 1000, 30 // 定義新同名變量: 不在同⼀層次代碼塊。
println(&s, z)
}
輸出:
0x2210230f30
0x2210230f30 20
0x2210230f18 30
1.2 常量
常量值必須是編譯期可确定的數字、字元串、布爾值。
const x, y int = 1, 2 // 多常量初始化
const s = "Hello, World!" // 類型推斷
const ( // 常量組
a, b = 10, 100
c bool = false
)
func main() {
const x = "xxx" // 未使⽤局部常量不會引發編譯錯誤。
}
不⽀持 1UL、2LL 這樣的類型字尾。
在常量組中,如不提供類型和初始化值,那麼視作與上⼀常量相同。
const (
s = "abc"
x // x = "abc"
)
常量值還可以是 len、cap、unsafe.Sizeof 等編譯期可确定結果的函數傳回值。
const (
a = "abc"
b = len(a)
c = unsafe.Sizeof(b)
)
如果常量類型⾜以存儲初始化值,那麼不會引發溢出錯誤。
const (
a byte = 100 // int to byte
b int = 1e20 // float64 to int, overflows
)
枚舉
關鍵字 iota 定義常量組中從 0 開始按⾏計數的⾃增枚舉值。
const (
Sunday = iota // 0
Monday // 1,通常省略後續⾏表達式。
Tuesday // 2
Wednesday // 3
Thursday // 4
Friday // 5
Saturday // 6
)
const (
_ = iota // iota = 0
KB int64 = 1 << (10 * iota) // iota = 1
MB // 與 KB 表達式相同,但 iota = 2
GB
TB
)
在同⼀常量組中,可以提供多個 iota,它們各⾃增⻓。
const (
A, B = iota, iota << 10 // 0, 0 << 10
C, D // 1, 1 << 10
)
如果 iota ⾃增被打斷,須顯式恢複。
const (
A = iota // 0
B // 1
C = "c" // c
D // c,與上⼀⾏相同。
E = iota // 4,顯式恢複。注意計數包含了 C、D 兩⾏。
F // 5
)
可通過⾃定義類型來實作枚舉類型限制。
type Color int
const (
Black Color = iota
Red
Blue
)
func test(c Color) {}
func main() {
c := Black
test(c)
x := 1
test(x) // Error: cannot use x (type int) as type Color in function argument
test(1) // 常量會被編譯器⾃動轉換。
}
1.3 基本類型
更明确的數字類型命名,⽀持 Unicode,⽀持常⽤資料結構。
類型 ⻓度 預設值 說明
bool 1 false
byte 1 0 uint8
rune 4 0 Unicode Code Point, int32
int, uint 4 或 8 0 32 或 64 位
int8, uint8 1 0 -128 ~ 127, 0 ~ 255
int16, uint16 2 0 -32768 ~ 32767, 0 ~ 65535
int32, uint32 4 0 -21億 ~ 21 億, 0 ~ 42 億
int64, uint64 8 0
float32 4 0.0
float64 8 0.0
complex64 8
complex128 16
uintptr 4 或 8 ⾜以存儲指針的 uint32 或 uint64 整數
array 值類型
struct 值類型
string "" UTF-8 字元串
slice nil 引⽤類型
map nil 引⽤類型
channel nil 引⽤類型
interface nil 接⼝
function nil 函數
⽀持⼋進制、⼗六進制,以及科學記數法。标準庫 math 定義了各數字類型取值範圍。
a, b, c, d := 071, 0x1F, 1e9, math.MinInt16
空指針值 nil,⽽⾮ C/C++ NULL。
1.4 引⽤類型
引⽤類型包括 slice、map 和 channel。它們有複雜的内部結構,除了申請記憶體外,還需
要初始化相關屬性。
内置函數 new 計算類型⼤⼩,為其配置設定零值記憶體,傳回指針。⽽ make 會被編譯器翻譯
成具體的建立函數,由其配置設定記憶體和初始化成員結構,傳回對象⽽⾮指針。
a := []int{0, 0, 0} // 提供初始化表達式。
a[1] = 10
b := make([]int, 3) // makeslice
b[1] = 10
c := new([]int)
c[1] = 10 // Error: invalid operation: c[1] (index of type *[]int)
有關引⽤類型具體的記憶體布局,可參考後續章節。
1.5 類型轉換
不⽀持隐式類型轉換,即便是從窄向寬轉換也不⾏。
var b byte = 100
// var n int = b // Error: cannot use b (type byte) as type int in assignment
var n int = int(b) // 顯式轉換
使⽤括号避免優先級錯誤。
*Point(p) // 相當于 *(Point(p))
(*Point)(p)
<-chan int(c) // 相當于 <-(chan int(c))
(<-chan int)(c)
同樣不能将其他類型當 bool 值使⽤。
a := 100
if a { // Error: non-bool a (type int) used as if condition
println("true")
}