Go 语言拥有一些不需要进行导入操作就可以使用的内置函数。它们有时可以针对不同的类型进行操作,例如:len、cap 和 append,或必须用于系统级的操作,例如:panic。因此,它们需要直接获得编译器的支持。

以下是一个简单的列表

名称 说明
close 用于管道通信
len、cap len 用于返回某个类型的长度或数量(字符串、数组、切片、map 和管道);cap 是容量的意思,用于返回某个类型的最大容量(只能用于切片和 map)
new、make new 和 make 均是用于分配内存:new 用于值类型和用户定义的类型,如自定义结构,make 用于内置引用类型(切片、map 和管道)。它们的用法就像是函数,但是将类型作为参数:new(type)、make(type)。new(T) 分配类型 T 的零值并返回其地址,也就是指向类型 T 的指针(详见第 10.1 节)。它也可以被用于基本类型:v := new(int)。make(T) 返回类型 T 的初始化之后的值,因此它比 new 进行更多的工作,new() 是一个函数,不要忘记它的括号**
copy、append 用于复制和连接切片
panic、recover 两者均用于错误处理机制
complex、real、imag 用于创建和操作复数

close()

close函数是用于关闭通道的

官方解释:

close函数是一个内建函数, 用来关闭channel,这个channel要么是双向的, 要么是只写的(chan<- Type)。
这个方法应该只由发送者调用, 而不是接收者。 当最后一个发送的值都被接收者从关闭的channel(下简称为c)中接收时,
接下来所有接收的值都会非阻塞直接成功,返回channel元素的零值。
如下的代码: 如果c已经关闭(c中所有值都被接收), x, ok := <- c, 读取ok将会得到false。

package main

import "fmt"

func main() {
    ch := make(chan int, 5)

    for i := 0; i < 5; i++ {
        ch <- i
    }

    close(ch) // 关闭ch
    for i := 0; i < 10; i++ {
        e, ok := <-ch
        fmt.Printf("%v, %v\n", e, ok)

        if !ok {
            break
        }
    }
}

输出:

0, true
1, true
2, true
3, true
4, true
0, false

在close之后, 还可以读取, 不过在读取完之后, 再检测ok, 就是false了。

如果不进行关闭就会导致阻塞进而抛出死锁,如下:

package main

import "fmt"

func main() {
	ch := make(chan int, 5)

	for i := 0; i < 5; i++ {
		ch <- i
	}

	// close(ch) // 关闭ch
	for i := 0; i < 10; i++ {
		e, ok := <-ch
		fmt.Printf("%v, %v\n", e, ok)

		if !ok {
			break
		}
	}
}

结果如下:

0, true
1, true
2, true
3, true
4, true
fatal error: all goroutines are asleep - deadlock!

goroutine 1 [chan receive]:
main.main()
        main.go:14 +0xa6
exit status 2
  • 注意事项:
    对于值为nil的channel或者对同一个channel重复close, 都会panic, 关闭只读channel会报编译错误。

代码示例如下:

关闭值为nil的通道

var c4 chan int

// 运行时错误:panic: close of nil channel
close(c4)
1234

重复关闭同一个通道

c3 := make(chan int, 1)
close(c3)

// 运行时错误:
// panic: close of closed channel
close(c3)
123456

关闭只读通道

c3 := make(<-chan int, 1)

// 编译错误:
// invalid operation: close(c3) (cannot close receive-only channel)
close(c3)
12345

正确的用法

c1 := make(chan int, 1) // 双向通道 (bidirectional)
c2 := make(chan<- int, 1) // 只写的 (send-only)
close(c1)
close(c2)

len()

len()用于计算数组(包括数组指针)、切片(slice)、map、channel、字符串等数据类型的长度,注意,结构休(struct)、整型布尔等不能作为参数传给len函数。

  1. 数组或数组指针:返回元素个数
  2. map和slice: 元素个数
  3. channel:通道中未读的元素个数
  4. 字符串:字节数,并非字符串的字符数
  5. 当V的值为nil值,len返回0
sl := make([]int,0)
sl = nil
if sl == nil{
    fmt.Println(len(sl)) //当slice类型为nil时,输出0
}

s := "欢迎学习Go的len()函数"//14个字符

fmt.Println(len(s)) //一个汉字占3个字节,所以这里输入28

在处理字符串时,经常需要知道字符串的字符数,但len()只计算字符串字节数,因此我们可以自定义处理字符串个数的函数。

//rune是32位的int别外,可以代表一个unicode字符,因此,通过将字符串将成rune类型的切片,切片元素个数代表字符个数
func count(str string) int {
    r := []rune(str)
    return len(r)
}

s := "欢迎学习Go的len()函数"//14个字符

fmt.Println(count(s)) //14

cap()

cap主要是为了让slice提供可变长度。

试想,如果没有cap,只有len。

sliceA长度为10,len=10,已经插满元素;现在要插入第十一个元素。

做法

sliceA的长度扩展为20,len=20,此时有用元素为11个,还有9个空位。

sliceA对外界暴露出来的接口只有ptr和len=20,此时如果需要再插入一个元素。

到底sliceA应该扩展呢还是可以继续在原来的基础直接插入呢,同时应该从哪个index插入呢?

这些问题在有了len和cap的组合之后就可以迎刃而解了。

cap()可以用来查看数组或slice的容量

在数组中由于长度固定不可变,因此len(arr)和cap(arr)的输出永远相同

在slice中,len(sli)表示可见元素有几个(也即直接打印元素看到的元素个数),而cap(sli)表示所有元素有几个,比如:

arr := []int{2, 3, 5, 7, 11, 13}
sli := arr[1:4]
fmt.Println(sli)
fmt.Println(len(sli))
fmt.Println(cap(sli))

cap()函数返回的是数组切片分配的空间大小。

package main  

import "fmt"  

func main() {   
    
    mySlice := make([]int, 5, 10) 
    
    fmt.Println("len(mySlice):", len(mySlice))   
    fmt.Println("cap(mySlice):", cap(mySlice)) 
}

输出结果为:

len(mySlice): 5
cap(mySlice): 10

new()

new 的作用是根据传入的类型分配一片内存空间并返回指向这片内存空间的指针

通过 new(T) 可以创建 T 类型的变量(这里 T 表示类型),初始值为 T 类型的零值,返回值为其地址(地址类型是 *T)

package main

import "fmt"

func main() {
    // 创建一个未命名的 int 类型变量,初始值是 0,返回值 p 是指向 int 类型变量的指针。
    p := new(int)
    fmt.Println(p, *p) // 0xc00001c0b8 0
    // 创建一个未命名的 string 类型变量,初始值是 "", 返回值是 q 是指向 string 类型变量的指针。
    q := new(string)
    fmt.Println(q, *q) // 0xc000010240 ""

    *q = "a"
    fmt.Println(*q) // a
}

make()

make() 是 Go 语言内存分配的内置函数,默认有三个参数。

make(Type, len, cap)

Type:数据类型,必要参数,Type 的值只能是 slice、 map、 channel 这三种数据类型。
len:数据类型实际占用的内存空间长度,map、 channel 是可选参数,slice 是必要参数。
cap:为数据类型提前预留的内存空间长度,可选参数。所谓的提前预留是当前为数据类型申请内存空间的时候,提前申请好额外的内存空间,这样可以避免二次分配内存带来的开销,大大提高程序的性能。
为了能更好的理解这些参数的含义,我们先来看下 make() 的三种不同用法:

//只传类型,不指定实际占用的内存空间和提前预留的内存空间,适用于 map 和 channel 
make(map[string]string)

//指定实际占用的内存空间为 2,不指定提前预留的内存空间
make([]int, 2)

//指定实际占用的内存空间为 2,指定提前预留的内存空间是 4
make([]int, 2, 4)

copy()

copy内置函数将元素从源片复制到目标片。

func copy(dst, src []Type) int
用于将源slice的数据(第二个参数),复制到目标slice(第一个参数)。

例子:

package main

import "fmt"

func main() {

	var a = []int{0, 1, 2, 3, 4, 5, 6, 7}
	var s = make([]int, 6)
	
	//源长度为8,目标为6,只会复制前6个
	n1 := copy(s, a)
    fmt.Println("s : ", s)
    fmt.Println("n1 : ", n1)
    
	//源长为7,目标为6,复制索引1到6
	n2 := copy(s, a[1:])
    fmt.Println("s : ", s)
    fmt.Println("n2 : ", n2)

}

输出结果:

s :  [0 1 2 3 4 5]
n1 :  6           
s :  [1 2 3 4 5 6]
n2 :  6  

append()

append内置函数将元素追加到片的末尾。如果它有足够的容量,则重新分配目的地以容纳新元素。如果没有,将分配一个新的底层数组。

func append(slice []Type, elems ...Type) []Type

append用法:

1、slice = append(slice, elem1, elem2)
2、slice = append(slice, anotherSlice...)

例子:

package main

import "fmt"

func main() {

	var a []string
	b := append(a, "a")
	fmt.Println(b)
	
	c := append(b, "b", "c", "d", "e")
	fmt.Println(c)
	 
	x := []int {1,2,3}
	y := []int {4,5,6}
	
	fmt.Println(append(x,4,5,6))
	fmt.Println(append(x,y...));

}

输出结果:

[a]
[a b c d e]  
[1 2 3 4 5 6]
[1 2 3 4 5 6]

panic()和recover()

func panic(v interface{})
假如函数F中书写了panic语句,会终止其后要执行的代码,在panic所在函数F内如果存在要执行的defer函数列表,按照defer的逆序执行。
返回函数F的调用者G,在G中,调用函数F语句之后的代码不会执行,假如函数G中存在要执行的defer函数列表,按照defer的逆序执行,这里的defer 有点类似 try-catch-finally 中的 finally。

func recover() interface{}
恢复内置功能允许程序管理恐慌的goroutine的行为。执行一个调用来恢复一个延迟的函数(但不是由它调用的任何函数)将停止恐慌序列y恢复正常执行,并检索传递给all panic的错误值。如果在deferred函数之外调用recover,它将不会停止一个令人恐慌的序列。

简单来讲:go中可以抛出一个panic的异常,然后在defer中通过recover捕获这个异常,然后正常处理。

例子:

package main
 
import "fmt"
 
func main() {
	fmt.Println("c")
     defer func() { // 必须要先声明defer,否则不能捕获到panic异常
        fmt.Println("d")
        if err := recover(); err != nil {
           fmt.Println(err) // 这里的err其实就是panic传入的内容
        }
        fmt.Println("e")
     }()
     f() //开始调用f
     fmt.Println("f") //这里开始下面代码不会再执行
}

func f() {
     fmt.Println("a")
     panic("异常信息")
     fmt.Println("b") //这里开始下面代码不会再执行
}

输出结果:

c
a       
d       
异常信息
e  

注意:利用recover处理panic指令,defer必须在panic之前声明,否则当panic时,recover无法捕获到panic。

complex()、real()和imag()

func complex(r, i FloatType) ComplexType
func real(c ComplexType) FloatType
func imag(c ComplexType) FloatType

使用内置的 complex 函数构建复数,并使用 real 和 imag 函数返回复数的实部和虚部:

例子:

package main

import (
	"fmt"
)

func main() {
	var x complex128 = complex(1, 2) // 1+2i
	var y complex128 = complex(3, 4) // 3+4i
	fmt.Println(x*y)                 // "(-5+10i)"
	fmt.Println(real(x*y))           // "-5"
	fmt.Println(imag(x*y))           // "10"
}

输出结果:

(-5+10i)
-5
10