函数
函数首字母大写时其他包可见,小写时只有相同包可以访问。
func funcName(param-list)(result-list) {
function-body
}
- 函数可以没有入参,也可以没有返回值
- 多个相邻的相同类型参数可以简写 // a int, b int 简写为 a,b int
- 支持有名的返回值,默认初始化为类型零值
- 不支持默认值参数
- 不支持函数重载
- 不支持命名函数嵌套定义。
- 支持多值返回
- 函数实参到形参的传递是值拷贝。
package main
import (
"fmt"
)
func main() {
e := 10
e = chvalue(e)
fmt.Print("e1= ")
fmt.Println(e) // 11
chpointer(&e)
fmt.Print("e2= ")
fmt.Println(e) // 12
}
func chvalue(a int) int {
a = a + 1
return a
}
func chpointer(a *int) {
*a = *a + 1
return
}
不定参数:
- 所有不定参数的类型必须相同
- 不定参数必须是函数的最后一个参数
- 不定参数名在函数体内相当于切片,适用于切片操作
- 切片可以作为参数传递给不定参数,切片名后要加 …
- 形参为不定参数的函数和形参为切片的函数类型不相同。
函数类型:
函数类型是函数定义首行去掉函数名,参数名和 {}
两个函数类型相同的条件是有相同的形参列表和返回值列表(列表元素的次序、个数和类型都相同),形参名可以不同。
defer
defer关键字用于注册延迟调用,这些调用以先进后出的顺序在函数返回前被执行。
defer后必须是函数或方法调用,不能是语句,否则会报 expression in defer must be function call错误。
defer函数的实参在注册时通过值拷贝传递,必须先注册再执行,如果defer位于return之后则不执行,主动退出(os.Exit(int)
)时不执行。
一般defer语句放在错误检查语句后。
闭包
闭包是由函数及其相关引用环境组合而成的实体,一般通过在匿名函数中引用外部函数或包全局变量构成。闭包=函数+引用环境。
闭包对闭包外的环境引入是直接引用,编译器会检测到闭包,会将闭包引用的外部变量分配到堆上。
如果函数返回的闭包引用了该函数的局部变量(参数或函数内部变量):
- 多次调用该函数,返回的多个闭包所引用的外部变量是多个副本,原因是每次调用函数都会为局部变量分配内存。
- 用一个闭包函数多次,如果该闭包修改了其引用的外部变量,则每一次调用该闭包对该外部变量都有影响,因为闭包函数共享外部引用。
举个例子:
package main
func fa(a int) func(i int) int {
return func(i int) int {
println(&a, a)
a = a + i
return a
}
}
func main() {
// f引用的外部闭包环境包括本次函数调用的形参a的值1
f := fa(1)
// g引用的外部闭包环境包括本次函数调用的形参a的值1
g := fa(1)
println(f(1))
// 多次调用f引用的是同一个副本a
println(f(1))
println(g(1))
println(g(1))
}
// 0xc00003df60 1
// 2
// 0xc00003df60 2
// 3
// 0xc00003df58 1
// 2
// 0xc00003df58 2
// 3
package main
var (
a = 0
)
func fa() func(i int) int {
return func(i int) int {
println(&a, a)
a = a + i
return a
}
}
func main() {
// f引用的外部闭包环境包括全局变量a的值1
f := fa()
// g引用的外部闭包环境包括全局变量a的值1
g := fa()
// f,g引用的是同一个副本a
println(f(1))
println(f(1))
println(g(1))
println(g(1))
}
// 0x982e00 0
// 1
// 0x982e00 1
// 2
// 0x982e00 2
// 3
// 0x982e00 3
// 4
对象是附有行为的数据,闭包是附有数据的行为。
panic和recover
这两个函数用来处理Go运行时错误。panic用来主动抛出错误,recover用来捕获panic抛出的错误。
函数签名如下:
panic(i interface{})
recover() interface{}
程序主动调用和产生运行时错误时都可以引发panic。发生panic后,程序会从调用panic的函数位置或者发生panic的地方立即返回,逐层向上执行函数的defer语句,然后逐层打印函数调用堆栈,直到被recover捕获或运行到最外层函数而退出。
recover只有在defer后面的函数体内被直接调用才能捕获panic终止异常,否则返回nil,异常继续向外传递。
defer func() {
println("defer inner")
recover()
}
可以有连续多个panic被抛出,但只能捕获最后一次panic.init函数引发的panic只能在init函数中被捕获。函数不能捕获内部新启动的goroutine抛出的panic.
init和main函数
init:
- init函数是用于程序执行前做包的初始化的函数,比如初始化包里的变量等
- 每个包可以拥有多个init函数
- 包的每个源文件也可以拥有多个init函数
- 同一个包中多个init函数的执行顺序go语言没有明确的定义(说明)
- 不同包的init函数按照包导入的依赖关系决定该初始化函数的执行顺序
- init函数不能被其他函数调用,而是在main函数执行之前,自动被调用
main:
// Go语言程序的默认入口函数(主函数):func main()
// 函数体用{}一对括号包裹。
func main(){
//函数体
}
执行顺序:
- 同一文件中,从上至下
- 同一个包中,按文件名字符串比较从小到大
- 不同包中,如果没有相互依赖则先import的后调用,如果存在依赖,则先调用最早依赖的package中的init.