golang 函数

函数

定义

//函数名首字母小写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)

特殊函数

  1. 至少有一个main函数

  2. 定义init和main函数,无参无返回

  3. 包初始化

    img

init

  1. init不能直接被调用。main函数执行前,引入包,package包初始化调用。
  2. 包内可以多个init,单文件可以多个init
  3. 同包文件执行顺序随机,同文件init执行自上而下。
  4. 不同包init执行根据引入依赖关系自顶而下。无依赖根据import自上而下。
//go文件扫描顺序:全局变量定义->init函数->main函数
func init()  {
	fmt.Println("我是init,在main函数前执行...")
}

main

  1. 包名为main程序方可允许
  2. 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

print

代补充

prinln

代补充

cap

代补充

len

代补充

append

详见 数据类型,slice

copy

详见 数据类型,slice

close

代补充

delete

代补充

make

  1. make(T,args)用来分配silice/map/channel内存,初始化的类型为T的值。

    func make(t Type, size ...IntegerType) Type
    
    1. make(slice,len,cap),len表示长度,cap表示容量
    2. make(map,[len]),len可选
    3. make(chan type,[len]),len可选,存在表示带缓冲区,没有则为无缓冲
  2. T不是零值和new不一样

new

  1. new(T)用来分配一个T类型被置零的内存地址指针,返回值为*T

  2. *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
    
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Mars'Ares

请我喝杯咖啡吧

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值