在这篇文章中,我们来学习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,长度:%dn", s1, cap(s1), len(s1))
fmt.Printf("s2的地址:%p,容量:%d,长度:%dn", 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,长度:%dn", s3, cap(s3), len(s3))
fmt.Printf("s4的地址:%p,容量:%d,长度:%dn", 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函数只能用于获得数组go数组,数组指针,切片,channel以及字符串这五种类型的长度。
如果是数组或者数组指针,len函数返回的是数组元素数量:
a := [5]int{1, 2, 3, 4, 5}
p := &a
fmt.Printf("数组长度:%d,指针数组长度:%dn", 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,数组的容量:%dn",len(a),cap(a))
fmt.Printf("切片的长度:%d,切片的容量:%dn",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()函数的值go数组,同样会触发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和clear3个内置函数,以后的文章中我们再来介绍这几个函数的用法吧。
限 时 特 惠: 本站每日持续更新海量各大内部创业教程,一年会员只需98元,全站资源免费下载 点击查看详情
站 长 微 信: muyang-0410