01
介绍
Go 语言为了方便我们开发,提供了 15 个内置函数,比如 len
、cap
、make
和 new
等。
本文我们结合 Go 内置函数官方文档[1],介绍一下 Go 语言中的内置函数。
02
内置函数
内置函数append
:
内置函数 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
:
内置函数 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
:
内置函数 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
:
内置函数 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
:
内置函数 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
:
内置函数 make
仅限为 slice
、map
和 channel
分配内存并初始化。
func make(t Type, size ...IntegerType) Type
make
第一个参数是类型,而不是值;第二个参数是可选(变长)参数,整型类型的值,返回值是该类型的值本身。
需要注意的是,第一个参数不同(不同类型),第二个参数的含义不同。
slice
第一个参数是切片类型,第二个参数的含义是指定切片的长度。如果没有传递第三个参数(整型类型的值),切片的容量等同于切片的长度,否则,切片的容量等同于第三个参数的值,需要注意的是,切片的容量必须不小于切片的长度。map
分配一个有足够空间可以容纳指定数量元素的空映射,第二个参数可以省略,如果省略第二个参数,将分配一个起始值 0。channel
指定缓冲区大小,初始化通道,如果第二个参数省略,或指定值为 0,该通道将被初始化为一个无缓冲通道。
内置函数 new
:
内置函数 new
也可以分配内存,与 make
的区别是,它仅分配内存,而未初始化。
和 make
相同,第一个参数是类型,而不是值;
和 make
不同,返回值是新分配的类型零值的指针。
内置函数 complex
:
内置函数 complex
将两个浮点型的值构造为一个复合类型的值,需要注意的是,实部和虚部必须是相同类型,即都是 float32
或 float64
。
返回值是对应的复合类型,即 complex64
对应 float32
或 complex128
对应 float64
。
内置函数 real
:
内置函数 real
用于返回复合类型的值的实部,返回值是对应的浮点数类型。
内置函数 imag
:
内置函数 imag
用于返回复合类型的值的虚部,返回值是对应的浮点数类型。
注意:complex
、real
和imag
三个内置函数,一般不常用,读者朋友们只需简单了解即可。
内置函数 close
:
内置函数 close
关闭通道,被关闭的通道必须是一个双向通道或仅支持发送的单向通道。
并且 close
应该由发送者执行,结果是在最后一个发送的值被接收后,关闭该通道。
通道被关闭后,任何该通道的接收者将返回成功而不会阻塞,接收者得到的返回值是该通道的类型零值和一个布尔类型的零值 false
。
需要注意的是,不仅是关闭通道会返回 false
,空通道也会返回 false
。
内置函数 panic
:
内置函数 panic
停止当前 goroutine
正常执行,当一个函数 F
调用 panic
时,该函数 F
立即停止正常执行。
该函数 F
通过 defer
延迟调用的任意函数,仍然会执行,并将执行结果返回给 F
调用者。
对于 F
的调用者 F2
,调用 F
也会像调用 panic
,停止 F2
的执行,并运行 F2
通过 defer
延迟调用的任意函数。以此类推,一直持续到当前 goroutine
中的所有函数都以相反的顺序停止运行。
此时,程序以非 0 退出代码终止运行。
以上终止程序运行的序列称为“恐慌”,可以通过接下来我们要介绍的内置函数 recover
进行控制。
内置函数 recover
:
内置函数 recover
允许程序管理“恐慌”的 goroutine
的行为。
可以在 defer
中调用 recover
恢复正常执行来停止“恐慌”,并且检索导致“恐慌”的错误。
但是,如果在 defer
之外调用 recover
,它不会恢复正常执行来停止“恐慌”。此种情况,recover
的返回值是 nil
。此外,当前执行 recover
的 goroutine
未“恐慌”,或调用 panic(nil)
时,recover
的返回值也是 nil
。
因此,我们可以通过 recover
的返回值,判断当前 goroutine
是否“恐慌”。
注意:此处讲的在defer
中调用recover
,是指在defer
本身中,而不是任何被defer
调用的函数中。
内置函数 print
:
内置函数 print
可以通过指定格式来格式化其参数,并将结果输出。
内置函数 println
:
内置函数 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