ch14、函数-可变参数和defer

ch14、函数 - 可变参数和defer

1、可变参数

传入的参数不需要指定个数,使用:varName …type表示

package function

import (
	"testing"
)

func Sum(ops ...int) int {
	ret := 0
	for _, op := range ops {
		ret += op
	}
	return ret
}

func TestVarParameter(t *testing.T) {
	t.Log(Sum(1, 2, 3, 4, 5))   // 15
	t.Log(Sum(1, 2, 3, 4))  // 10
}

2、defer 函数(延迟函数)

作用:延迟函数,一般是用于释放资源或者收尾工作;执行动作是在函数return之后,因此作为资源释放作用再好不过

package function
import (
	"fmt"
	"testing"
)

func Clear() {
	fmt.Println("Clear resource...")
}

func TestDefer(t *testing.T) {
	defer Clear()
	fmt.Println("Start...")
	panic("Error...")	// defer仍然会执行
	fmt.Println("after panic is executed?")	// 不会被执行
}

defer 释放资源『避坑指南』:

  • 资源释放动作一定紧跟资源使用(打开、连接)语句,不然defer可能不会被执行到,导致内存泄露
package function

import (
	"os"
)

// 关闭文件
func read() error {
	// 错误用法
	f1, err := os.OpenFile("filename", os.O_RDWR, os.ModePerm)
	if err != nil {
		return err
	}
	// 此时,defer还没执行到,提前return了,无效defer导致内存泄露
	defer f1.Close()

	// 正确用法,紧跟资源使用语句
	f2, err := os.OpenFile("filename", os.O_RDWR, os.ModePerm)
	defer f2.Close()
	if err != nil {
		return err
	}
	return err
}
  • 多个 defer 调用顺序:

    LIFO(后入先出,先进后出),defer后的操作可以理解为压入栈中

package defertest
import (
	"fmt"
	"testing"
)

func TestExecuteSequence(t *testing.T) {
	defer func() { fmt.Println("1") }()
	defer func() { fmt.Println("2") }()
	defer func() { fmt.Println("3") }()
	t.Log(4)
}
/*
output:
4
3
2
1
*/
  • defer,return,return var(函数返回变量值,如return 1,但返回变量早就定义好了) 执行顺序:

    首先return,其次return var,最后defer。defer可以修改函数最终返回值

注意:被defer的函数可以读取和修改带名称的返回值

package defertest

import (
	"fmt"
	"testing"
)

func deferFunc1(i int) (t int) {
	t = i
	defer func() {
		t += 1
	}()
	return t
}

func deferFunc2(i int) int {	// 没有返回参数,会直接返回t=1的值到临时变量
	t := i
	defer func() {	// 最后会计算该函数,然后将值2返回,但是没有变量接受
		t += 1
	}()
	return t
}

func deferFunc3(i int) (t int) {
	defer func() {  // 被defer的函数可以读取和修改带名称的返回值
		t += i
	}()
	return 1
}

// 给返回值指定了一个名称叫做t, return时会自动将函数体内部t作为返回值
// 其实本质就是提前定义了一个局部变量t, 在函数体中使用的t就是这个局部变量, 返回的也是这个局部变量
func deferFunc4() (t int) {
	defer func(i int) {
		fmt.Println(i)
		fmt.Println(t)
	}(t)	// defer在定义时变量就会被确定,此处的t是取的int默认值0,即入参i=0
	t = 0
	return 1	// 为什么t会变成1?返回变量早就被声明了,所以会是1
}

func TestDeferFunc(t *testing.T) {
	// 猜猜下面输出的内容和顺序
	fmt.Println(deferFunc1(1))
	fmt.Println(deferFunc2(1))
	fmt.Println(deferFunc3(1))
	deferFunc4()
}

/*
output:
2
1
2
0
1
*/
  • deferred函数的参数(是指入参):

    被 deferred 函数的参数在defer声明时就已经被确定

package defertest

import (
	"fmt"
	"testing"
)

func calc(index string, a, b int) int {
	ret := a + b
	fmt.Println(index, a, b, ret)
	return ret
}

func TestCalcParameter(t *testing.T) {
	a := 1
	b := 2
	defer calc("1", a, calc("10", a, b))
	a = 0	// defer不会使用该参数值
	return
}
/*
output:
10 1 2 3
1 1 3 4
*/

3、init函数

golang里面有两个保留的函数:

  • init函数(能够应用于所有的package)
  • main函数(只能应用于package main)
  • 这两个函数在定义时不能有任何的参数和返回值
  • go程序会自动调用init()和main(),所以**不能**在任何地方调用这两个函数
  • package main必须包含一个main函数, 但是每个package中的init函数都是可选的
  • 一个package里面可以写任意多个init函数,但这无论是对于可读性还是以后的可维护性来说,**强烈建议用户在一个package中每个文件只写一个init函数
  • 单个包中代码执行顺序如下
    • main包-->常量-->全局变量-->init函数-->main函数-->Exit
package main

import "fmt"

const constValue = 998        // 1
var gloalVarValue int = abc() // 2
func init() { // 3
	fmt.Println("执行main包中main.go中init函数")
}
func main() { // 4
	fmt.Println("执行main包中main.go中main函数")
}
func abc() int {
	fmt.Println("执行main包中全局变量初始化")
	return 998
}
  • 多个包之间代码执行顺序如下
    在这里插入图片描述
  • init函数的作用
    • init函数用于处理当前文件的初始化操作, 在使用某个文件时的一些准备工作应该放到这里
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值