5.26笔记

47 阅读3分钟

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 仅当信道的缓冲区填满后,向其发送数据时才会阻塞。当缓冲区为空时,接受方会阻塞。

修改示例填满缓冲区,然后看看会发生什么。