Go 语言入门指南:基础语法和常用特性解析 | 豆包MarsCode AI刷题

116 阅读4分钟

作为一个多年C++学习者,记录一下学go的过程中遇到的很多go语言独特的难以记忆的内容

GO中常用的格式化占位符

  • 字符串%s,表示字符串。
  • 整数%d(十进制),%x(十六进制),%o(八进制),%b(二进制)。
  • 浮点数%f(小数形式),%e(科学计数法),%g(最简表示法)。
  • 控制浮点数的精度:可以通过 %.[数字]f 来控制小数点后的位数。
  • 布尔值%t,表示布尔值(truefalse)。
  • 通用格式%v,无需知道变量的类型,直接打印变量的值;%+v,打印结构体时显示字段名;%#v,显示变量的 Go 语法表示。
  • 类型%T,直接打印出变量的类型

引用类型

在 Go 中,引用类型是指可以存储和引用数据位置(内存地址)的类型。这些类型允许多个变量共享同一块数据,因为它们都指向同一个内存地址,而不是独立存储数据的副本。Go 中的引用类型包括以下几种:

  • 指针(Pointer)
  • 切片(Slice)
arr := [5]int{1,2,3,4,5}
slice := arr[1:4]
// sclice是arr的切片,它引用了arr当中的一部分元素,修改slice也会修改arr
  • 映射(Map)
  • 通道(Channel)
func greet(roc <-chan string) {
    fmt.Println("Hello " + <-roc ,"!")
}
​
func main() {
    fmt.Println("Main Start")
    c := make(chan string)
    go greet(c)
    c <- "Demo"
    fmt.Println("Main Stop")
}
​
  • 接口(Interface)

select语句

select 关键字的语法和作用都和其他语言当中的switch语句非常类似,但是它主要用于处理多个通道操作的情况。它允许 goroutine 等待多个通道中的任何一个操作(发送或接收),并在其中一个通道准备好的时候执行相应的操作。select 吞噬了在处理多个通道时的复杂性,是实现并发程序的一个重要工具

select {
case expr1:
    // channel1 准备好,处理 expr1
case expr2:
    // channel2 准备好,处理 expr2
case expr3:
    // channel3 准备好,处理 expr3
default:
    // 如果没有通道准备好,执行这个语句
}
for i := 0; i < 2; i++ {
    select {
    case msg1 := <-ch1:
        fmt.Println(msg1)
    case msg2 := <-ch2:
        fmt.Println(msg2)
    case msg3 := <-ch3:
        fmt.Println(msg3)
    default:
        fmt.Println("没有通道准备好")
    }
}
​
  • select 会轮询每一个 case 分支,检查通道是否准备好
  • 如果有通道已经准备好(即有数据可接收),就会执行相应的 case 分支,并接收数据
  • 如果所有通道都未准备好,则执行 default 分支,输出 "没有通道准备好"
  • 循环进行了两次,这意味着将会接收两个通道的数据(因为 i < 2 的限制)。

单次的select只会进行一次的选择,只有将select语句配合for循环去使用,才能够保证持续的去监听通道,如果所有的 case 语句(通道操作)被阻塞,那么 select 语句将阻塞直到这些 case 条件的一个不阻塞(通道操作),case 块执行。如果有多个 case 块(通道操作)都没有阻塞,那么运行时将随机选择一个不阻塞的 case 块立即执行

nil值

在 Go 中,nil 是一种特殊的零值,表示一个“空”或“未初始化”的状态。它可以用于多种类型的值,包括指针、切片、映射、通道和接口。每种类型的 nil 都有独特的行为,尤其是在通道中,nil 通道有一些重要特性和注意事项

nil 是一个预定义标识符,用于表示指针、通道、映射、切片和接口等引用类型的零值。通常用来表示一个变量没有被初始化或指向空的内存地址,我的理解就是相当于把C++当中的0,NULL, nullptr的特殊的零值做了一个统一,简化了程序员的操作。

var p *int   // p 是一个 nil 指针
var s []int  // s 是一个 nil 切片
var m map[string]int  // m 是一个 nil 映射
var ch chan int // ch 是一个 nil 通道

nil通道的行为

与已初始化的通道不同,nil 通道的特性非常独特,任何操作都会被永久阻塞。它的行为如下:

  • 发送:对 nil 通道进行发送操作会阻塞,程序不会继续执行。
  • 接收:对 nil 通道进行接收操作也会阻塞,程序不会继续执行。
  • 关闭:对 nil 通道进行关闭操作会导致运行时异常(panic)。

示例:

var ch chan int // ch 是 nil 通道// 发送操作会阻塞
go func() {
    ch <- 1 // 会阻塞在这里,永远不会发送成功
}()
​
// 接收操作会阻塞
go func() {
    <-ch // 会阻塞在这里,永远不会接收到数据
}()

nil通道可以灵活的用于控制select分支的执行