Go 学习笔记

本书不定期更新,可以到 github.com/qyuhen 下载最新版。

目录
第一部分 Go 语言 9
第 1 章 基础 10
1.1 变量 10
1.2 基本类型 11
1.3 类型转换 13
1.4 常量 13
1.5 字符串 15
1.6 运算符 20
1.7 指针 21
1.8 保留字 23

  1. 控制结构 23
    1.10 自定义类型 27
    1.11 初始化 2
    1.12 内置函数 30
    第 2 章 函数 31
    2.1 函数类型 31
    2.2 多返回值、命名返回参数 32
    2.3 变参 33
    2.4 匿名函数、闭包 33
    2.5 Defer 35
    2.6 Panic、Recover 37
    2.7 Call Stack 38
    第 3 章 Array、Slices 和 Maps 40
    3.1 Array 40
    4
    3.2 Slices 42
    3.3 Maps 45
    第 4 章 Structs 50
    4.1 定义 50
    4.2 初始化 51
    4.3 匿名字段 52
    4.4 方法 56
    4.5 内存布局 61
    4.6 字段标签 62
    第 5 章 接⼝口 64
    5.1 接⼝口定义 64
    5.2 执行机制 65
    5.3 匿名字段方法 69
    5.4 空接⼝口 70
    5.5 类型推断 71
    5.6 接⼝口转换 72
    第 6 章 并发 73
    6.1 Goroutine 73
    6.2 Channel 75
    第 7 章 程序结构 84
    7.1 源文件 84
    7.2 包 85
    7.3 测试 88
    第 8 章 进阶 92
    8.1 运行时 92
    8.2 内存分配 92
    5
    8.3 内存布局 93
    8.4 反射 96
    8.5 cgo 99
    第 9 章 ⼯工具 101
    9.1 命令行⼯工具 101
    9.2 GDB 调试 104
    9.3 条件编译 107
    9.4 跨平台编译 107
    第二部分 标准库 109
    第 10 章 io 110
    10.1 Interface 110
    10.2 Text File 110
    10.3 Binary File 111
    10.4 Pipe 112
    10.5 Encoding 113
    10.6 Buffer 114
    10.7 Temp 115
    10.8 Path 116
    第 11 章 strings 120
    11.1 strconv 120
    11.2 strings 121
    11.3 template 121
    11.4 regexp 126
    第 12 章 compress 131
    12.1 zlib 131
    12.2 zip 132
    6
    第 13 章 flag 134
    第 14 章 crypto 137
    14.1 md5 137
    14.2 des 137
    14.3 rsa 138
    第 15 章 encoding 143
    15.1 json 143
    第 16 章 net 146
    16.1 tcp 146
    16.2 http 147
    16.3 rpc 150
    第 17 章 syscall 154
    17.1 fork 154
    17.2 daemon 155
    第 18 章 time 157
    18.1 Time 157
    18.2 Duration 158
    18.3 Timer 159
    第 19 章 sync 161
    19.1 Locker 161
    19.2 Cond 163
    19.3 Once 165
    19.4 WaitGroup 166
    19.5 atomic 167
    第 20 章 os 168
    20.1 System 168
    7
    20.2 Environ 169
    20.3 Process 170
    20.4 Signal 172
    20.5 User 174
    第三部分 扩展库 175
    mgo - mongoDB Driver 176
    snappy - Compress/Decompress Library 181
    附录 182
    8
    第一部分 Go 语言
    暂时不知道写啥……
    代码测试环境:
    • Go Version 1.0.3
    • MacBook Pro, 4GB, Mac OS X Lion 10.8.2
    9
    第 1 章 基础
    1.1 变量
    使用 var 定义变量,自动初始化为零值 (Zero Value)。
    • bool: false
    • integers: 0
    • floats: 0.0
    • string: ""
    • pointers, functions, interfaces, slices, channels, maps: nil
    变量类型总是放在变量名后面,但可以省略 (根据初始化值进行类型推断)。Go 是强类型语言,类型
    推断只是一种简便的代码语法糖,不同于 javascriptpython,我们不能修改变量的类型。
    var a = 1234
    var b string = "hello"
    var c bool
    func main() {
    println(a, b, c)
    }
    在函数内部,甚至可以省略 var 关键字,用 ":=" 这种更短的表达式完成变量类型推断和初始化。
    a := 1
    可以一次声明多个变量,并对其赋予初始值。
    var x, y int // 类型相同的多个变量
    var ( // 类型不同的多个变量
    a int
    b bool
    )
    var c, d int = 1, 2 // 指定类型,多个变量类型相同。
    var e, f = 123, "hello" // 自动推断,多个变量类型按初始值推断。
    g, h := 123, "hello" // 自动推断,多个变量类型按初始值推断。
    多变量赋值时,将先计算所有相关值,然后 left-to-right 进行变量赋值。
    sa := []int{1, 2, 3}
    i := 0
    i, sa[i] = 1, 2 // sets i = 1, sa[

& 0010 = 2 AND, 都为 1
| 1111 = 15 OR, 至少一个为 1
^ 1101 = 13 XOR, 只能一个为 1
&^ 0100 = 4 AND NOT, bit clear。从 a 上清除所有 b 的标志位。
11 在 0、1、3 上设置了标志,检查 6 这三个二进制位,如果是 1 就改为 0 即可。
演示:
func main() {
a := 0
20
a = a | (1 << 3) // 在 bit3 上设置标志位 (从 bit0 开始算)
a = a | (1 << 6) // 在 bit6 上设置标志位
println(a) // a = 72 = 0100 1000
a = a &^ (1 << 6)// 清除 bit6 上的标志位,a = 8 = 0000 0100
println(a)
}
不支持运算符重载 (某些内建类型,如 string 做了 "+" 运算符重载)。
在 Go 中 ++、-- 是 语句 (statement) 而非 表达式 (expression)。另外,"p++" 表示 "(p)++"
而非 "(p++)" 。
func main() {
i := 0
p := &i
//a := i++ // syntax error: unexpected ++, expecting semicolon or newline or }
i++
a := i
//b := (
p)++ // syntax error: unexpected ++, expecting semicolon or newline or }
*p++
b := *p
println(a, b)
}
Go 所有的操作符和分隔符都在这了。

  • & += &= && == = ( )
  • | -= |= || < <= [ ]
  • ^ = ^= <- > >= { }
    / << /= <<= ++ = := , ;
    % >> %= >>= -- ... . :
    &^ &^=
    1.7 指针
    Go 保留了指针,
    T 表示 T 对应的指针类型。如果包含包名,则应该是 ".T"。代表指
    针类型的符号 "
    " 总是和类型放在一起,而不是紧挨着变量名。同样支持指针的指针 (**T)。
    可以确保下面两个变量都是指针类型,没有 C/C++ 里面那些弯弯绕的注意事项。
    var a, b int
    • 操作符 "&" 取变量地址,用 "
    " 透过指针变量间接访问⺫⽬目标对象。
    • 默认值是 nil,没有 NULL 常量。
    21
    • 不支持指针运算,不支持 "->" 运算符,直接用 "." 选择符操作指针⺫⽬目标对象成员。
    • 可以在 unsafe.Pointer 和任意类型指针间进行转换。
    • 可以将 unsafe.Ponter 转换为 uintptr,然后做变相指针运算。uintptr 可以转换为整数。
    type User struct {
    Id int
    Name string
    }
    func main() {
    i := 100
    var p *int = &i // 取地址
    //p++ // invalid operation: p += 1 (mismatched types int and int)
    println(
    p) // 取值
    up := &User{ 1, "Jack" }
    up.Id = 100 // 直接操作指针对象成员
    fmt.Println(up)
    u2 := up // 拷⻉贝对象
    u2.Name = "Tom"
    fmt.Println(up, u2)
    }
    输出:
    100
    &{100 Jack}
    &{100 Jack} {100 Tom}
    通过 unsafe.Pointer 在不同的指针类型间转换,做到类似 C void
    的用途。
    const N int = int(unsafe.Sizeof(0))
    func main() {
    x := 0x1234
    p := unsafe.Pointer(&x) // int -> Pointer
    p2 := (
    [N]byte)(p) // Pointer -> [4]int,注意 slice 的内存布局和 array 是不同的。
    // 数组类型元素长度必须是常量。
    for i, m := 0, len(p2); i < m; i++ {
    fmt.Printf("%02X ", p2[i])
    }
    }
    输出:
    34 12 00 00
    将 unsafe.Pointer 转换成 uintptr,变相做指针运算。当然,还得用 Pointer 转换回来才能取值。
    type User struct {
    Id int
    Name string
    22
    }
    func main() {
    p := &User{ 1, "User1" }
    var np uintptr = uintptr(unsafe.Pointer(p)) + unsafe.Offsetof(p.Name)
    var name string = (string)(unsafe.Pointer(np))
    println(
    name)
    }
    输出:
    User1
    如果基于安全需要,用 -ldfalgs "-u" 参数阻⽌止编译 unsafe 代码。
    1.8 保留字
    Go 的保留字不多,整个语言设计相当简洁。
    break default func interface select
    case defer go map struct
    chan else goto package switch
    const fallthrough if range type
    continue for import return var
    1.9 控制结构
    Go 代码控制结构非常简洁,只有 for、switch、if。
    1.9.1 IF
    if 语句只需记住:
    • 条件表达式没有括号;
    • 支持一个初始化表达式 (可以是多变量初始化语句);
    • 左大括号必须和条件语句在同一行。
    func main() {
    a := 10
    if a > 0 { // 左大括号必须写在这,否则被解释为 "if a > 0;" 导致编译出错。
    a += 100
    } else if a == 0 { // 注意左大括号位置。
    a = 0
    } else { // 注意左大括号位置。
    a -= 100
    }
    23
    println(a)
    }
    支持单行模式。
    if a > 0 { a += 100 } else { a -= 100 }
    初始化语句中定义的都是 block 级别的局部变量,不能在 block 外面使用 (else 分支内有效,但会
    隐藏 block 外的同名变量)。
    if err := file.Chmod(0664); err = nil {
    log.Stderr(err)
    return err
    }
    很遗憾,Go 不提供三元操作符 "?:",也没办法用 python "and or",因为不是 bool。
    Go 编译器有个小小的 Bug,下面的代码会引发编译错误 (或许是有意这么做的)。
    func test(b bool) string {
    if b {
    return "Y"
    } else {
    return "N"
    }
    }
    输出
    function ends without a return statement
    只能改成下面这样。
    func test(b bool) string {
    if b {
    return "Y"
    }
    return "N"
    }
    1.9.2 For
    for 支持三种形式:
    for init; condition; post {}
    for condition {}
    for {}
    24
    初始化和步进表达式可以是多个值。
    for i, j := 0, len(a)-1; i < j; i, j = i+1, j-1 {
    a[i], a[j] = a[j], a[i]
    }
    每次循环都会重新检查条件表达式,如果达表达式包含函数调用,则可能被执行多次。建议用初始
    化表达式一次性计算。
    func main() {
    l := func(s string) int {
    println("get length")
    return len(s)
    }
    ss := "abcd"
    for i := 0; i < l(ss); i++ {
    println(ss[i])
    }
    println("---------")
    for i, m := 0, l(ss); i < m; i++ {
    println(ss[i])
    }
    }
    输出:
    get length
    97
    get length
    98
    get length
    99
    get length
    100
    get length

get length
97
98
99
100
for 循环和保留字 range 一起使用,完成 迭代器 (iterator) 操作。除了 array,range 表达式是在循
环前预先计算好的,在此期间可以安全修改迭代器对象。
func main() {
for i, c := range "abc" {
fmt.Printf("s[%d] = %c\n", i, c)
}
}
25
输出:
s[