介绍
Go 语言为了方便我们开发,提供了 15 个内置函数,比如 len
、cap
、make
和 new
等。
本文我们结合 Go 内置函数官方文档[1],介绍一下 Go 语言中的内置函数。
内置函数
内置函数 append
可以将元素追加到切片的末尾。
func append(slice []Type, elems ...Type) []Type
当我们使用 append
向切片中追加元素时,切片的底层数组必须具有足够的容量,否则,将会分配一个新的底层数组。
func main() { s := []int{1, 2, 3} fmt.Printf("%p %d\n", s, s) s = append(s, 4) fmt.Printf("%p %d\n", s, s) }
输出结果:
0xc0000b2018 [1 2 3] 0xc0000ae030 [1 2 3 4]
所以,我们需要注意的是,append
之后的切片赋值给同一个变量。
除了使用 append
向切片中追加元素之外,我们还可以向切片中追加另一个切片,例如:
s1 := []int{5, 6, 7} s = append(s, s1...)
此外,还可以使用 append
将字符串追加到字节切片中,例如:
str := "hello " bs := append([]byte(str), "world"...)
内置函数 copy
可以将源切片中的元素拷贝到目标切片。
func main() { src := []string{"go", "vue"} dst := make([]string, 2) n := copy(dst, src) fmt.Printf("%s %d\n", dst, n) }
输出结果:
[go vue] 2
copy
的返回值是拷贝元素的个数,返回值是 len(src)
和 len(dst)
的最小值。
需要注意的是,源切片和目标切片中的元素可能会重叠。
此外,还可以使用 copy
将一个字符串中的字节拷贝到一个字节切片中,例如:
func main() { str := "hello" bs := make([]byte, 5) n := copy(bs, str) fmt.Printf("%s %d\n", bs, n) }
内置函数 delete
通过指定键 m[key]
删除 map
中的元素。
如果 map
是 nil
或没有元素,delete
不做任何操作。
func main() { var m map[int]string fmt.Println(m) delete(m, 0) fmt.Println(m) m1 := make(map[int]string) fmt.Println(m1) delete(m1, 0) fmt.Println(m1) m2 := make(map[int]string, 2) m2[0] = "hello" m2[1] = "world" fmt.Println(m2) delete(m2, 0) fmt.Println(m2) }
输出结果:
map[] map[] map[] map[] map[0:hello 1:world] map[1:world]
内置函数 len
返回值的长度,值的类型不同,值的长度含义也不同。
array
数组中元素的个数。*array
数组指针中元素的个数,即使数组指针的值是nil
。slice
和map
切片或映射中元素的个数,如果切片或映射的值是nil
,len(v)
值的长度是 0。string
字符串中字节的个数。channel
通道缓冲区中未读元素的个数,如果缓冲通道的值是nil
,len(v)
值的长度是 0。
func main() { arr := [3]int{1, 2, 3} fmt.Println(arr) fmt.Println(len(arr)) var arr1 *[3]int fmt.Println(arr1) fmt.Println(len(arr1)) var s []int fmt.Println(len(s)) s = []int{1, 2, 3} fmt.Println(len(s)) var m map[int]string fmt.Println(len(m)) m = make(map[int]string) m[0] = "hello" fmt.Println(len(m)) str := "frank" fmt.Println(len(str)) var c chan int fmt.Println(c) fmt.Println(len(c)) c = make(chan int) fmt.Println(len(c)) }
输出结果:
[1 2 3] 3 <nil> 3 0 3 0 1 5 <nil> 0 0
需要注意的是,slice
、map
和 channel
必须先使用内置函数 make
初始化后,该类型的值才可以使用。
内置函数 cap
返回值的容量,值的类型不同,值的容量含义也不同。
array
数组中元素的个数,数组的cap(v)
与len(v)
相等。*array
数组指针中元素的个数,数组指针的cap(v)
和len(v)
相等。slice
切片可以容纳元素的最大长度,如果切片的值是nil
,该切片cap(v)
值的容量是 0。channel
通道缓冲区的容量,如果通道的值是nil
,该通道cap(v)
值的容量是 0。
func main() { var arr [3]int fmt.Println(arr) fmt.Println(cap(arr)) var arr1 *[3]int fmt.Println(arr1) fmt.Println(cap(arr1)) var s []string fmt.Println(s) fmt.Println(cap(s)) s = make([]string, 1) s[0] = "go" fmt.Println(s) fmt.Println(cap(s)) var c chan int fmt.Println(c) fmt.Println(cap(c)) }
输出结果:
[0 0 0] 3 <nil> 3 [] 0 [go] 1 <nil> 0
内置函数 make
仅限为 slice
、map
和 channel
分配内存并初始化。
func make(t Type, size ...IntegerType) Type
make
第一个参数是类型,而不是值;第二个参数是可选(变长)参数,整型类型的值,返回值是该类型的值本身。
需要注意的是,第一个参数不同(不同类型),第二个参数的含义不同。
slice
第一个参数是切片类型,第二个参数的含义是指定切片的长度。如果没有传递第三个参数(整型类型的值),切片的容量等同于切片的长度,否则,切片的容量等同于第三个参数的值,需要注意的是,切片的容量必须不小于切片的长度。map
分配一个有足够空间可以容纳指定数量元素的空映射,第二个参数可以省略,如果省略第二个参数,将分配一个起始值 0。channel
指定缓冲区大小,初始化通道,如果第二个参数省略,或指定值为 0,该通道将被初始化为一个无缓冲通道。
内置函数 new
也可以分配内存,与 make
的区别是,它仅分配内存,而未初始化。
和 make
相同,第一个参数是类型,而不是值;
和 make
不同,返回值是新分配的类型零值的指针。
内置函数 complex
将两个浮点型的值构造为一个复合类型的值,需要注意的是,实部和虚部必须是相同类型,即都是 float32
或 float64
。
返回值是对应的复合类型,即 complex64
对应 float32
或 complex128
对应 float64
。
内置函数 real
用于返回复合类型的值的实部,返回值是对应的浮点数类型。
内置函数 imag
用于返回复合类型的值的虚部,返回值是对应的浮点数类型。
注意:
complex
、real
和imag
三个内置函数,一般不常用,读者朋友们只需简单了解即可。
内置函数 close
关闭通道,被关闭的通道必须是一个双向通道或仅支持发送的单向通道。
并且 close
应该由发送者执行,结果是在最后一个发送的值被接收后,关闭该通道。
通道被关闭后,任何该通道的接收者将返回成功而不会阻塞,接收者得到的返回值是该通道的类型零值和一个布尔类型的零值 false
。
需要注意的是,不仅是关闭通道会返回 false
,空通道也会返回 false
。
内置函数 panic
停止当前 goroutine
正常执行,当一个函数 F
调用 panic
时,该函数 F
立即停止正常执行。
该函数 F
通过 defer
延迟调用的任意函数,仍然会执行,并将执行结果返回给 F
调用者。
对于 F
的调用者 F2
,调用 F
也会像调用 panic
,停止 F2
的执行,并运行 F2
通过 defer
延迟调用的任意函数。以此类推,一直持续到当前 goroutine
中的所有函数都以相反的顺序停止运行。
此时,程序以非 0 退出代码终止运行。
以上终止程序运行的序列称为“恐慌”,可以通过接下来我们要介绍的内置函数 recover
进行控制。
内置函数 recover
允许程序管理“恐慌”的 goroutine
的行为。
可以在 defer
中调用 recover
恢复正常执行来停止“恐慌”,并且检索导致“恐慌”的错误。
但是,如果在 defer
之外调用 recover
,它不会恢复正常执行来停止“恐慌”。此种情况,recover
的返回值是 nil
。此外,当前执行 recover
的 goroutine
未“恐慌”,或调用 panic(nil)
时,recover
的返回值也是 nil
。
因此,我们可以通过 recover
的返回值,判断当前 goroutine
是否“恐慌”。
注意:此处讲的在
defer
中调用recover
,是指在defer
本身中,而不是任何被defer
调用的函数中。
内置函数 print
可以通过指定格式来格式化其参数,并将结果输出。
内置函数 println
可以通过指定格式来格式化其参数,并将结果输出。与 print
的区别是,参数之间会添加空格,末尾会添加换行符。
注意:
println
与标准库fmt
中的fmt.Print()
和fmt.Println()
的区别是,前者是标准错误输出,后者是标准输出。在 Go 语言开发中,官方推荐使用标准库fmt
包,感兴趣的读者朋友们可以查阅相关资料进一步了解。
03
总结
本文我们介绍 Go 语言的内置函数,读者朋友们需要重点掌握的内置函数是 len
、cap
、make
、new
、append
、copy
、delete
、close
、panic
和 recover
。
Go 语言作为静态编程语言,分为编译期和运行时,Go 语言的内置函数的底层原理,感兴趣的读者朋友们可以阅读 Go 语言内置函数的源码[2]。
推荐阅读:
参考资料
[1]
Go 内置函数官方文档: https://pkg.go.dev/builtin@go1.20.3
[2]
Go 语言内置函数的源码: https://cs.opensource.google/go/go/+/refs/tags/go1.20.3:src/cmd/compile/internal/typecheck/universe.go