一文学透Go语言15个的内置函数(built-in)
学习Go语言的内置函数(built-in)。
什么是Go语言内置函数呢?
简单地理解就是指Go内置的不需要以包名为前缀就可以直接访问的函数:
//非内置函数
import "fmt"
fmt.Println("test")
//内置函数
println("test")
按目前Go最新的版本(1.20.5
),Go语言总共有15个内置函数,分别为:append,new,make,len,cap,copy,delete,panic,recover,close,complex,real,imag,print,println。
append
append
函数用于将一个或多个元素添加到切片(slice
)的末尾,其函数签名为:
func append(slice []Type, elems ...Type) []Type
append
函数第一个参数slice
是一个切片,elems
表示切片的元素,其数据类型与切片元素的数据类型必须相同:
s0 := []int{1, 2}
// 添加一个元素 s1 = []int{1, 2, 3}
s1 := append(s0, 3)
// 添加多个元素 s2 = []int{1, 2, 3, 4, 5, 6}
s2 := append(s1, 4, 5, 6)
// 将切片添加到另一个切片 s3 = []int{1, 2, 3, 4, 5, 6 , 1 , 2}
s3 := append(s2, s0...)
// 截取不同切片再合并 s4 = []int{[4 5 6 3 4 5 6 1 2]}
s4 := append(s3[3:6], s3[2:]...)
//空接口切片
var t []interface{}
//t == []interface{}{100, 6.8, "test"}
t = append(t, 100, 6.8, "test")
var b []byte
// 将字符串添加字节数组 b = []byte{'j', 'u', 's','t','','g','o'}
b = append(b, "just go"...)
append
函数往切片添加元素时,如果切片的容量还没满,那么返回的新切片与源切片指向同一个底层数组:
package main
import "fmt"
func main() {
//创建一个容量为2,长度为0的切片
s1 := make([]int, 0, 2)
//向s1添加两个元素
s2 := append(s1, 3, 4)
fmt.Printf("s1的地址:%p,容量:%d,长度:%d\n", s1, cap(s1), len(s1))
fmt.Printf("s2的地址:%p,容量:%d,长度:%d\n", s2, cap(s2), len(s2))
}
上面代码的运行结果如下所示,可以看出两个切片指向的底层数组地址是一致的:
s1的地址:0xc0000140a0,容量:2,长度:0
s2的地址:0xc0000140a0,容量:2,长度:2
如果切片的容量已经耗尽,那么append
函数会为切片重新分配一个更大容量的底层数组,并将数据复制过去,此时返回的切片则指向新分配的数组:
package main
import "fmt"
func main() {
//创建一个容量为2,长度为0的切片
s3 := make([]int, 0, 2)
//向s4添加两个元素
s4 := append(s3, 1, 2, 3, 4)
fmt.Printf("s3的地址:%p,容量:%d,长度:%d\n", s3, cap(s3), len(s3))
fmt.Printf("s4的地址:%p,容量:%d,长度:%d\n", s4, cap(s4), len(s4))
}
上面代码的运行结果如下,可以看到,向一个容量只有2的切片添加4个元素时,append
函数会自动扩容:
s3的地址:0xc0000140c0,容量:2,长度:0
s4的地址:0xc000020080,容量:4,长度:4
new
new
函数用于为指定的类型分配内存,并返回一个对应内存的指针,其函数签名如下:
func new(Type) *Type
使用new
分配的一般是普通数据类型(如:int
,float32
等)或复合类型(比如array
,struct
):
i := new(int)
*i = 10
fmt.Println(*i) //10
type User struct{
ID int
Name string
}
// 下面使用new创建结构体的语句等同于 u := &User{}
u := new(User)
make
make
函数用于创建并初始化slice
、channel
和map
这样的引用类型,其函数签名如下:
func make(t Type, size ...IntegerType) Type
与new
函数相似,make
的第一个参数是所要定义的数据类型,而与new
函数不同的是,make
函数并不是返回一个指针类型,这是因为slice
,channel
和map
是引用类型:
m := make(map[string]string)
当创建切片时,必须传入第二个参数来指定切片的长度:
s := make([]int,2)
也可以传入第三个参数来指定义切片的容量,如果不传第三个参数,则容量与长度相等:
//容量:5,长度:2
s1 := make([]int,2,5)
//未指定容量,因此容量与长度都等3
s2 := make([]string,3)
如果是创建map
类型时,那么就不需要传入第二个与第三个参数了:
m := make(map[string]string)
创建channel
对象时,make
函数的第二个参数用于指定channel
缓冲区的大小,如果没有传入,就表示创建了一个没有缓冲区的channel
:
//无缓冲区
c1 := make(chan int)
//缓冲区大小:2
c := make(chan int,2)
len与cap
len
函数用于获得指定类型的长度,其函数签名如下:
func len(v Type) int
len
函数只能用于获得数组,数组指针,切片,channel以及字符串这五种类型的长度。
如果是数组或者数组指针,len函数返回的是数组元素数量:
a := [5]int{1, 2, 3, 4, 5}
p := &a
fmt.Printf("数组长度:%d,指针数组长度:%d\n", len(a), len(p))
对于channel
类型,len
函数返回channel
当前缓冲区元素的个数:
package main
import (
"fmt"
"sync"
)
func main() {
//容量为10
ch := make(chan int, 10)
var w sync.WaitGroup
w.Add(1)
go func(ch chan int) {
ch <- 1
ch <- 2
ch <- 3
close(ch)
w.Done()
}(ch)
w.Wait()
fmt.Println(len(ch))//3
}
对于切片,len
函数返回则返回切片元素的数量:
//长度为2,容量为10的切片
s := make([]string,2,10)
fmt.Println("切片的长度:%d",len(s))//2
对于字符串,len
函数返回的是字符串的字节数,而不是字符串的字符数,因为len
把字符串当作字节数组来处理:
fmt.Println(len("学习Go语言"))//14
fmt.Println(len([]byte("学习Go语言"))) //14
cap
函数用于获得指定类型的容量,其函数的签名如下:
func cap(v Type) int
对于数组或者数组指针,容量与长度是相等的,因此cap
函数与len
函数的返回值是相等:
0 <= len(s) == cap(s)
而对于切片与channel
类型,则返回其容量,并且容量大于长度,切片与channel
的容量与长度的关系为:
0 <= len(s) <= cap(s)
cap
函数用法示例:
a := [5]int{1,2,3,4,5}
s := make([]int,2 10)
fmt.Printf("数组的长度:%d,数组的容量:%d\n",len(a),cap(a))
fmt.Printf("切片的长度:%d,切片的容量:%d\n",len(s),cap(s))
copy
copy函数用于将源切片src
复制到目标切片dst
中,并返回复制元素的个数,其函数签名如下:
func copy(dst, src []Type) int
源切片与目标切片的数据类型必须相同,且目标切片必须有对应的容量可以存储复制过来的元素:
package main
import "fmt"
func main() {
var s1 = make([]int, 1)
var s2 = []int{1, 2, 3}
i := copy(s1, s2)
fmt.Printf("复制了:%d个元素\n", i)
fmt.Println(s1)
}
上述程序的运行结果为:
复制了:1个元素
[1]
因为目标切片的容量为1,因此只复制了一个元素。
delete
delete
函数用于根据指定的key删除对应map
类型的元素,其函数签名如下:
func delete(m map[Type]Type1, key Type)
当map
为nil
或者要删除的key
不存在时,调用delete
并不会引发什么错误:
package main
import "fmt"
func main() {
var bookstore = map[string]float32{
"Go入门": 100,
"Go Web编程": 200,
}
delete(bookstore, "Go入门")
//删除不存在的key,并不会报错
delete(bookstore, "编程思想")
fmt.Println(bookstore)
}
panic与recover
在Go语言,如果触发了panic
而没有及时捕获,那么就意味着程序崩溃,有很多错误会触发panic
,比如**数组访问越界(index out of bounds)或者除以0(division by zero)**等。
除了系统自动触发的panic
,我们也可以使用panice()
函数手动触发panic
,panic()
函数签名如下:
func panic(v any)
panic()
函数可以接收一个任意类型的值,甚至可以传入nil
,如果上层有异常捕获,则这个值最终会被上层捕获:
panic(nil)
当然,如果给panic()
传入nil
,当上层捕获异常时,会很奇怪,明明有异常,为什么得到的信息是nil,所以在后续的版本中,Go如果将nil
作为panic()
函数的值,同样会触发panic
。
recover
函数与defer
语句结合,是捕获panic
的唯一手段,recover
函数的返回值就是传给panic()
函数的值:
package main
import "fmt"
func myFunc(){
panic("触发panic")
}
func main() {
protect(myFunc)
}
func protect(f func()) {
defer func() {
msg := recover()
fmt.Println(msg)
}()
f()
}
close
close
函数用于关闭channel
,不过close
只能关闭双向channel
与用于发送数据的channel
,向一个只用于接收数据的channel
发送数据触发panic
:
//双向
var ch1 chan int
//用于发送数据
var ch2 chan<- int
//用于接收数据
var ch3 <-chan int
close(ch1)
close(ch2)
close(ch3) //报错
complex,real,imag
complex
函数用于构建一个复数,其作用与直接声明一个complex128
类型相同:
package main
import "fmt"
func main() {
var c1 complex128 = 2 + 10i
var c2 = complex(2, 10)
if c1 == c2 {
fmt.Println("c1与c2相等")
}
}
real
函数用于获得复数的实部,imag
函数用于获得复数的虚部:
fmt.Println(real(c1)) // 2
fmt.Println(imag(c1)) // 10
print与println
print
与println
函数用于将所有参数向控制台输出,两者唯一的区别在于println()
会在每个参数中间加一个空格,并且最后会换行:
print(1,2,3,4) //1234
println(1,2,3,4,5) //1 2 3 4
不过,一般很少使用这两个函数,因为标准库fmt
包同样可以输出,并且功能更加强大。
小结
好了,至此我们就学完了15
个Go语言的内置函数,其实,还未发布的Go1.21
版本新增了max
,min
和clear
3个内置函数,以后的文章中我们再来介绍这几个函数的用法吧。