目录
函数
定义
//函数名首字母小写private 大写public
//不支持默认值
//不定参数只能有一个并且在最后一个
func function_name( [parameter list] [... type]) [return_types] {
函数体
[return 返回值列表]
}
参数
位置参数
//定义
func plus(n1 int){
fmt.Println(n1+1)
}
//使用
n1 := 10
plus(n1)
默认值/可选参数
没有~,可基于函数闭包实现,等函数式编程一节再讲。
关键字参数
没有~,可基于map实现
可变参数
//可变参数放到位置参数后面
function_name(args... type)
//可变参数,形参的类型为切片
func sum(nums... int) {
fmt.Printf("形参nums的类型为:%T\n",nums)
all := 0
for _,num := range nums{
all += num
}
fmt.Println(all)
}
//使用
sum(3,4,5)
sum(1,2,3,4,5)
参数传递
值传递
数字、字符串、布尔、数组、结构体struct
引用传递(地址拷贝)
指针、切片slice、map、管道、interface
其实都是值传递,关键在于拷贝了值还是地址
func mix(num int,flag bool, str string, arr [3]int, p Person,slice []int,kwarg map[string]string) {
num++
fmt.Println("mix() num:",num)
flag = false
fmt.Println("mix() flag:",flag)
str = "wanna change"
fmt.Println("mix() str:",str)
arr[0] = 99
fmt.Println("mix() arr:",arr)
p.Name = "Frank"
fmt.Println("mix() p:",p)
slice[0] = 99
fmt.Println("mix() slice:",slice)
kwarg["lady"] = "killer"
fmt.Println("mix() kwarg",kwarg)
}
//例子
num := 3
flag := true
str := "lady_killer9"
arr := [3]int{}
p := Person{Name:"frankyu"}
slice:=[]int{1,2,3}
kwarg := map[string]string{"lady":"9"}
mix(num,flag,str,arr,p,slice,kwarg)
fmt.Println("main() num:",num)
fmt.Println("main() flag:",flag)
fmt.Println("main() str:",str)
fmt.Println("main() arr:",arr)
fmt.Println("main() p:",p)
fmt.Println("main() slice:",slice)
fmt.Println("main() kwarg:",kwarg)
返回值
多个返回值
// 函数定义
func get_sum(n1,n2 int) (int,int) {
return n1+n2, n1-n2
}
//使用
fmt.Println(get_sum(n1,10))
返回值命名
//函数定义
func get_plus(n1,n2 int) (sum int, differ int){
sum = n1+n2
differ = n1-n2
return
}
//使用
fmt.Println(get_plus(a,b))
注意项
- 首字母大写就可以被其他包使用,类似C++的Public
- 基本数据类型和数组是值传递,函数内修改不会对函数外的变量产生影响
- 需要通过函数修改外部变量时,可以传递地址,用指针做形参
- 函数不支持传统的函数重载
- 函数也是一个数据类型,可以将函数赋给其他变量
- 支持对函数返回值命名
匿名函数
// 没有名字的函数,只使用一次
res := func (n int,m int) int{
return n+m
}(10,20)
特殊函数
-
至少有一个main函数
-
定义init和main函数,无参无返回
-
包初始化
init
- init不能直接被调用。main函数执行前,引入包,package包初始化调用。
- 包内可以多个init,单文件可以多个init
- 同包文件执行顺序随机,同文件init执行自上而下。
- 不同包init执行根据引入依赖关系自顶而下。无依赖根据import自上而下。
//go文件扫描顺序:全局变量定义->init函数->main函数
func init() {
fmt.Println("我是init,在main函数前执行...")
}
main
- 包名为main程序方可允许
- main函数同包名只能有一个
//包名为main
package main
//函数名 main
func main(){
//函数体
}
内置函数
defer
- 调用时机:函数或方法在返回或结束前执行
- 多次调用:先写的后调用,栈的顺序
- 传参问题:值类型时,参数不改变后使用defer、defer的函数不修改则传地址、匿名函数函数体中使用
- 优点:
- panic后执行defer,防止异常时忘记释放资源
- 函数复杂分支返回,写一次即可,简洁,复用性好
//defer有个栈,先入后出,入栈时连同变量
func deferUse() {
defer fmt.Println("defer begin...but display last")
defer fmt.Println("defer end...but display first")
}
src->runtime->runtime2.go
//_defer是一个单链表(链栈),采用头插的方式,取的时候先取头的。
type _defer struct {
siz int32 // 包含参数和结果
started bool // 是否开始
heap bool // 是否分配在堆上
openDefer bool // 是否开放编码
sp uintptr // 栈指针
pc uintptr // 程序计数器
fn *funcval // 开放编码时可为nil
_panic *_panic // 正在运行的defer的panic
link *_defer // _defer指针
fd unsafe.Pointer // 预分配的函数数据
varp uintptr
framepc uintptr
}
src->cmd->compoile->internal->gc->ssa.go stmt方法的一个case
case ODEFER:
if Debug_defer > 0 {
var defertype string
if s.hasOpenDefers {
defertype = "open-coded"
} else if n.Esc == EscNever {
defertype = "stack-allocated"
} else {
defertype = "heap-allocated"
}
Warnl(n.Pos, "%s defer", defertype)
}
if s.hasOpenDefers {
s.openDeferRecord(n.Left)
} else {
d := callDefer
if n.Esc == EscNever {
d = callDeferStack
}
s.callResult(n.Left, d)
}
执行顺序
1. defer是LIFO,类似堆栈方式运行的
示例
func test(){
defer fmt.Println(1)
defer fmt.Println(2)
defer fmt.Println(3)
return
}
运行结果
3
2
1
2. return与defer同时出现时,defer只有被return包裹才会运行
func test2(){
defer fmt.Println(1)
defer fmt.Println(2)
if true {
return
}
defer fmt.Println(3)
return
}
运行结果
2
1
3. return变量时,并不是原子操作,分为赋值 + 返回,如果有defer将在赋值与返回间执行
匿名返回值
func test3() int {
var i int
defer i++
return i
}
//等价
//func test3() int {
// var i int
// ret:=i
// defer i++
// return ret
//}
运行结果
0
有名返回值
func test4() (i int) {
defer i++
return i
}
//等价
//func test4() (i int) {
// defer i++
// return i
//}
运行结果
1
有名返回值临时赋值
func test3() (i int) {
var temp int
defer temp++
return temp
}
//等价
//func test3() (i int) {
// i=temp
// defer temp++
// return i
//}
运行结果
0
panic
- panic后执行本协程的defer
- 跨协程问题使用recover解决
src->runtime->runtime2.go
type _panic struct {
argp unsafe.Pointer // 指向defer栈的函数指针
arg interface{} // panic参数
link *_panic // 先前panic的指针
pc uintptr // 运行时,此panic被绕过时返回到哪
sp unsafe.Pointer // 运行时,此panic被绕过时返回到哪
recovered bool // 是否此panic结束
aborted bool // 这个panic被终止
goexit bool
}
src->runtime->panic.go
//核心代码 gopanic、fatalpanic两个函数
systemstack(func() {
exit(2)
})
单协程
//当panic异常发生时,程序会中断运行,并立即执行在该goroutine中被延迟的函数(defer机制)。随后,程序崩溃并输出日志信息。
func panicDefer() {
fmt.Println("code before panic")
defer fmt.Println("defer in panicDefer")
panic("something wrong in panic Defer")
fmt.Println("code after panic")
}
运行结果
code before panic
defer in panicDefer
panic: something wrong in panic Defer
goroutine 1 [running]:
main.panicDefer()
E:/Workspace/Go_workspace/learn_go/src/defer_panic_recover/main/main.go:42 +0x10a
main.main()
E:/Workspace/Go_workspace/learn_go/src/defer_panic_recover/main/main.go:62 +0x27
exit status 2
跨协程问题
//panic 只会触发当前 goroutine 的 defer,协程外面的defer执行不了
func panicGoroutine() {
defer println("defer in main")
go func() {
defer println("defer in goroutine")
panic("something wrong...")
}()
time.Sleep(time.Second)
}
运行结果
defer in goroutine
panic: something wrong...
goroutine 6 [running]:
main.panicGoroutine.func1()
E:/Workspace/Go_workspace/learn_go/src/defer_panic_recover/main/main.go:50 +0x78
created by main.panicGoroutine
E:/Workspace/Go_workspace/learn_go/src/defer_panic_recover/main/main.go:48 +0x78
exit status 2
recover
- 终止panic造成的崩溃
- 在defer中使用时才有效
src->runtime->panic.go
/*
recover 可以中止 panic 造成的程序崩溃,只能在 defer 中发挥作用
p==nil,也就是说你在panic前使用recover,没defer中,recover失效了。
p!=nil,在gopanic中会进行处理。
*/
func gorecover(argp uintptr) interface{} {
gp := getg()
p := gp._panic
if p != nil && !p.goexit && !p.recovered && argp == uintptr(p.argp) {
p.recovered = true
return p.arg
}
return nil
}
代码
func recoverDefer() {
//defer println("defer in main") // 执行不到
defer func() {
if err := recover();err!=nil{
println("defer in main")
println(err)
}
}()
go func() {
defer println("defer in goroutine")
panic("something wrong...")
}()
panic("something wrong in recoverDefer()")
time.Sleep(time.Second)
}
结果
defer in main
(0xf4d940,0xf85bc8)
defer in goroutine
代补充
prinln
代补充
cap
代补充
len
代补充
append
详见 数据类型,slice
copy
详见 数据类型,slice
close
代补充
delete
代补充
make
-
make(T,args)用来分配silice/map/channel内存,初始化的类型为T的值。
func make(t Type, size ...IntegerType) Type
- make(slice,len,cap),len表示长度,cap表示容量
- make(map,[len]),len可选
- make(chan type,[len]),len可选,存在表示带缓冲区,没有则为无缓冲
-
T不是零值和new不一样
new
-
new(T)用来分配一个T类型被置零的内存地址指针,返回值为*T
-
*T指向的值为T的零值
// The new built-in function allocates memory. The first argument is a type, // not a value, and the value returned is a pointer to a newly // allocated zero value of that type. func new(Type) *Type