1.1 變量
Go 是靜態類型語 ,不能在運 期改變變量類型。
使 關鍵字 var 定義變量, 動初始化為零值。如果提供初始化值,可省略變量類型,由
編譯器 動推斷。
var x in tvar 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
i, data[i] = 2, 100+l
// (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
println(&s, y)
{
s, z := 1000, 30
println(&s, z)
// 重新賦值: 與前 s 在同 層次的代碼塊中,且有新的變量被定義。
// 通常函數多返回值 err 會被重複使 。
// 定義新同名變量: 不在同 層次代碼塊。
}
輸出:
0x2210230f30
0x2210230f30 20
0x2210230f18 30
1.2 常量
常量值必須是編譯期可確定的數字、字符串、布爾值。
const x, y int = 1, 2
const s = "Hello, World!"
const (
// 多常量初始化
// 類型推斷
// 常量組
a, b
c= 10, 100
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 (
_
KB= iota
MB
GB
TB
int64 = 1
// iota = 0
// iota = 1
// 與 KB 表達式相同,但 iota = 2
)
在同 常量組中,可以提供多個 iota,它們各 增 。
const ( A, B = iota, iota
如果 iota 增被打斷,須顯式恢復。
const ( A= iota// 0 B= "c" // 1 C= iota// c D // c,與上 相同。 E // 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, 持常 數據結構。
持 進位、 六進位,以及科學記數法。標準庫 math 定義了各數字類型取值範圍。
a, b, c, d := 071, 0x1F, 1e9, math.MinInt16
空指針值 nil, C/C++ NULL。
1.4 引 類型
引 類型包括 slice、map 和 channel。它們有複雜的內部結構,除了申請內存外,還需
要初始化相關屬性。
內置函數 new 計算類型 ,為其分配零值內存,返回指針。 make 會被編譯器翻譯
成具體的創建函數,由其分配內存和初始化成員結構,返回對象 指針。
a := []int a[1] = 10 b := make([]int, 3) b[1] = 10 c := new([]int) c[1] = 10 // 提供初始化表達式。 // makeslice // Error: invalid operation: c[1] (index of type *[]int)
1.5 類型轉換
不 持隱式類型轉換,即便是從窄向寬轉換也不 。
var b byte = 100 // var n int = b var n int = int(b) // Error: cannot use b (type byte) as type int in assignment // 顯式轉換
使 括號避免優先級錯誤。
*Point(p) (*Point)(p)
同樣不能將其他類型當 bool 值使 。
a := 100 if a { println("true") // Error: non-bool a (type int) used as if condition }
1.6 字符串
字符串是不可變值類型,內部 指針指向 UTF-8 字節數組。
默認值是空字符串 ""。
索引號訪問某字節,如 s[i]。
不能 序號獲取字節元素指針,&s[i] 法。
不可變類型, 法修改字節數組。
字節數組尾部不包含 NULL。
//runtime.h struct String { byte* intgo str; len; };
使 索引號問字符 (byte)。
s := "abc" println(s[0] == '\x61', s[1] == 'b', s[2] == 0x63)
輸出:
true true true
使 "`" 定義不做轉義處理的原始字符串, 持跨 。
s := `a b\r\n\x00 c` println(s)
輸出:
a
b\r\n\x00
c
連接跨 字符串時,"+" 必須在上 末尾,否則導致編譯錯誤。
s := "Hello, " + "World!" s2 := "Hello, " + "World!" // Error: invalid operation: + untyped string
持 兩個索引號返回 串。 串依然指向原字節數組,僅修改了指針和 度屬性。
s := "Hello, World!" s1 := s[:5] // Hello s2 := s[7:] s3 := s[1:5] // Hello // World! // ello
單引號字符常量表 Unicode Code Point, 持 \uFFFF、\U7FFFFFFF、\xFF 格式。
對應 rune 類型,UCS-4。
func main() { fmt.Printf("%T\n", 'a') var c1, c2 rune = '\u6211', '們' println(c1 == '我', string(c2) == "\xe4\xbb\xac") }
輸出:
int32
// rune 是 int32 的別名
true true
要修改字符串,可先將其轉換成 []rune 或 []byte,完成後再轉換為 string。 論哪種轉
換,都會重新分配內存,並複製字節數組。
func main() { s := "abcd" bs := []byte(s) bs[1] = 'B' println(string(bs)) u := "電腦" us := []rune(u) us[1] = '話' println(string(us)) }
輸出:
aBcd
電話
for 循環遍歷字符串時,也有 byte 和 rune 兩種 式。
func main() fmt.Println() for _, r := range s { fmt.Printf("%c,", r) } // byte // rune }
輸出:
a,b,c, ,±,, , ,,
a,b,c,漢,字,
1.7 指針
持指針類型 *T,指針的指針 **T,以及包含包名前綴的 *.T。
默認值 nil,沒有 NULL 常量。
操作符 "&" 取變量地址,"*" 透過指針訪問 標對象。
不 持指針運算,不 持 "->" 運算符,直接 "." 訪問 標成員。
func main() var d = data var p *data p = &d fmt.Printf("%p, %v\n", p, p.a) }
輸出:
0x2101ef018, 1234
不能對指針做加減法等運算。
x := 1234 p := &x
// 直接 指針訪問 標對象成員, 須轉換。 p++ // Error: invalid operation: p += 1 (mismatched types *int and int)
可以在 unsafe.Pointer 和任意類型指針間進 轉換。
func main() { x := 0x12345678 p := unsafe.Pointer(&x) n := (*[4]byte)(p) for i := 0; i Pointer // Pointer -> *[4]byte }
輸出:
78 56 34 12
返回局部變量指針是安全的,編譯器會根據需要將其分配在 GC Heap 上。
func test() *int { x := 100 return &x // 在堆上分配 x 內存。但在內聯時,也可能直接分配在 標棧。 }
將 Pointer 轉換成 uintptr,可變相實現指針運算。
func main() { d := struct { s x string int }{"abc", 100} p := uintptr(unsafe.Pointer(&d)) p += unsafe.Offsetof(d.x) // *struct -> Pointer -> uintptr // uintptr + offset p2 := unsafe.Pointer(p) px := (*int)(p2) *px = 200 fmt.Printf("%#v\n", d) // uintptr -> Pointer // Pointer -> *int // d.x = 200 }
輸出:
struct { s string; x int }
注意:GC 把 uintptr 當成普通整數對象,它 法阻 "關聯" 對象被回收。
1.8 定義類型
可將類型分為命名和未命名兩 類。命名類型包括 bool、int、string 等, array、
slice、map 等和具體元素類型、 度等有關,屬於未命名類型。
具有相同聲明的未命名類型被視為同 類型。
具有相同基類型的指針。
具有相同元素類型和 度的 array。
具有相同元素類型的 slice。
具有相同鍵值類型的 map。
具有相同元素類型和傳送 向的 channel。
具有相同欄位序列 (欄位名、類型、標籤、順序) 的匿名 struct。
籤名相同 (參數和返回值,不包括參數名稱) 的 function。
法集相同 ( 法名、 法籤名相同,和次序 關) 的 interface。
var a struct { x int `a` } var b struct { x int `ab` } // cannot use a (type struct { x int "a" }) as type struct { x int "ab" } in assignment b = a
可 type 在全局或函數內定義新類型。
func main() { type bigint int64 var x bigint = 100 println(x) }
新類型不是原類型的別名,除擁有相同數據存儲結構外,它們之間沒有任何關係,不會持
有原類型任何信息。除 標類型是未命名類型,否則必須顯式轉換。
x := 1234 var b bigint = bigint(x) var b2 int64 = int64(b) var s myslice = []int var s2 []int = s // 必須顯式轉換,除 是常量。 // 未命名類型,隱式轉換。
test(1) // 常量會被編譯器 動轉換,AAAjiaoyuwang發送教學視頻。