defer defer 语句会将函数推迟到外层函数返回之后执行。
推迟调用的函数其参数会立即求值,但直到外层函数返回前该函数都不会被调用。
package main
import "fmt"
func main() { defer fmt.Println("world")
fmt.Println("hello")
} 1 2 3 4 5 6 7 8 9 hello world 1 2 推迟的函数调用会被压入一个栈中。
当外层函数返回时,被推迟的函数会按照后进先出的顺序调用。
package main
import "fmt"
func main() { fmt.Println("counting")
for i := 0; i < 10; i++ {
defer fmt.Println(i)
}
fmt.Println("done")
}
指针 Go 拥有指针。指针保存了值的内存地址。
类型 *T 是指向 T 类型值的指针。其零值为 nil。
var p *int 1 & 操作符会生成一个指向其操作数的指针。
i := 42 p = &i 1 2
- 操作符表示指针指向的底层值。
fmt.Println(*p) // 通过指针 p 读取 i *p = 21 // 通过指针 p 设置 i 1 2 这也就是通常所说的“间接引用”或“重定向”。
与 C 不同,Go 没有指针运算。
package main
import "fmt"
func main() { i, j := 42, 2701
p := &i // 指向 i
fmt.Println(*p) // 通过指针读取 i 的值
*p = 21 // 通过指针设置 i 的值
fmt.Println(i) // 查看 i 的值
p = &j // 指向 j
*p = *p / 37 // 通过指针对 j 进行除法运算
fmt.Println(j) // 查看 j 的值
}
结构体 一个结构体(struct)就是一组字段(field)。
package main
import "fmt"
type Vertex struct { X int Y int }
func main() { fmt.Println(Vertex{1, 2}) }
{1 2}
结构体字段使用点号来访问。
package main
import "fmt"
type Vertex struct { X int Y int }
func main() { v := Vertex{1, 2} v.X = 4 fmt.Println(v.X) }
信道 信道是带有类型的管道,你可以通过它用信道操作符 <- 来发送或者接收值。
ch <- v // 将 v 发送至信道 ch。 v := <-ch // 从 ch 接收值并赋予 v。
(“箭头”就是数据流的方向。)
和映射与切片一样,信道在使用前必须创建:
ch := make(chan int) 1 默认情况下,发送和接收操作在另一端准备好之前都会阻塞。这使得 Go 程可以在没有显式的锁或竞态变量的情况下进行同步。
以下示例对切片中的数进行求和,将任务分配给两个 Go 程。一旦两个 Go 程完成了它们的计算,它就能算出最终的结果。
package main
import "fmt"
func sum(s []int, c chan int) { sum := 0 for _, v := range s { sum += v } c <- sum // 将和送入 c }
func main() { s := []int{7, 2, 8, -9, 4, 0}
c := make(chan int)
go sum(s[:len(s)/2], c)
go sum(s[len(s)/2:], c)
x, y := <-c, <-c // 从 c 中接收
fmt.Println(x, y, x+y)
}
带缓冲的信道 信道可以是带缓冲的。将缓冲长度作为第二个参数提供给 make 来初始化一个带缓冲的信道:
ch := make(chan int, 100) 1 仅当信道的缓冲区填满后,向其发送数据时才会阻塞。当缓冲区为空时,接受方会阻塞。
修改示例填满缓冲区,然后看看会发生什么。