做者:林冠宏 / 指尖下的幽灵html
博客:http://www.cnblogs.com/linguanh/github
GitHub : https://github.com/af913337456/数组
腾讯云专栏: https://cloud.tencent.com/developer/user/1148436/activities闭包
虫洞区块链专栏:https://www.chongdongshequ.com/article/1536563643883.html函数
defer
是Go
语言中一个很重要的关键词。本文主要以简短的手法
列举出,它在不一样的多种常见代码片断中,所体现出来的不同的效果。从笔试的角度来看,能够说是覆盖了绝大部分题型。区块链
此外,在本文以前,还有本人另外一篇一样使用例子的形式
对 channel
数据类型作直观讲解
的文章。指针
Golang, 以17个简短代码片断,切底弄懂 channel 基础code
defer
调用的函数,且为非闭包函数状况defer
调用的函数,即便不使用闭包函数状况defer
调用的函数,且非闭包函数状况defer
调用闭包函数,且内调用外部非传参进来的变量的状况defer
调用闭包函数,若内部使用了传参参数的值的状况defer
所调用的非闭包函数,参数若是是函数的状况defer
不影响 return
的值defer
的影响return
或 panic
或 执行完毕
后被调用栈
的形式。先进后出,先定义的后被调用func Test_1(t *testing.T) { // defer 的调用顺序。由下到上,为 栈的形式。先进后出 defer0() // ↑ defer1() // | defer2() // | defer3() // | //defer4() // | defer5() // | defer6() // | defer7() // | defer8() // | 从下往上 }
defer
调用的函数,且为非闭包函数,值不会
受后面的改变影响func defer0() { a := 3 // a 做为演示的参数 defer fmt.Println(a) // 非引用传参,非闭包函数中,a 的值 不会 受后面的改变影响 a = a + 2 } // 控制台输出 3
defer
调用的函数,即便不使用闭包函数,值也会
受后面的改变影响func myPrintln(point *int) { fmt.Println(*point) // 输出引用所指向的值 } func defer1() { a := 3 // &a 是 a 的引用。内存中的形式: 0x .... ---> 3 defer myPrintln(&a) // 传递引用给函数,即便不使用闭包函数,值 会 受后面的改变影响 a = a + 2 } // 控制台输出 5
defer
调用的函数,且非闭包函数,值不会
受后面的改变影响func p(a int) { fmt.Println(a) } func defer2() { a := 3 defer p(a) // 传递值给函数,且非闭包函数,值 不会 受后面的改变影响 a = a + 2 } // 控制台输出: 3
defer
调用闭包函数,且内调用外部非传参进来的变量,值会
受后面的改变影响// 闭包函数内,事实是该值的引用 func defer3() { a := 3 defer func() { fmt.Println(a) // 闭包函数内调用外部非传参进来的变量,事实是该值的引用,值 会 受后面的改变影响 }() a = a + 2 // 3 + 2 = 5 } // 控制台输出: 5
// defer4 会抛出数组越界错误。 func defer4() { a := []int{1,2,3} for i:=0;i<len(a);i++ { // 同 defer3 的闭包形式。由于 i 是外部变量,没用经过传参的形式调用。在闭包内,是引用。 // 值 会 受 ++ 改变影响。致使最终 i 是3, a[3] 越界 defer func() { fmt.Println(a[i]) }() } } // 结果:数组越界错误
defer
调用闭包函数,若内部使用了传参参数的值。使用的是值func defer5() { a := []int{1,2,3} for i:=0;i<len(a);i++ { // 闭包函数内部使用传参参数的值。内部的值为传参的值。 defer func(index int) { fmt.Println(a[index]) // index == i }(i) // 后进先出,3 2 1 } } // 控制台输出: // 3 // 2 // 1
defer
所调用的非闭包函数,参数若是是函数,会按顺序先执行(函数参数)func calc(index string, a, b int) int { ret := a + b fmt.Println(index, a, b, ret) return ret } func defer6() { a := 1 b := 2 // calc 充当了函数中的函数参数。即便在 defer 的函数中,它做为函数参数,定义的时候也会首先调用函数进行求值 // 按照正常的顺序,calc("10", a, b) 首先被调用求值。calc("122", a, b) 排第二被调用 defer calc("1", a, calc("10", a, b)) defer calc("12",a, calc("122", a, b)) } // 控制台输出: /** 10 1 2 3 // 第一个函数参数 122 1 2 3 // 第二个函数参数 12 1 3 4 // 倒数第一个 calc 1 1 3 4 // 倒数第二个 calc */
defer
不影响 return
的值下面两个例子的结论是:htm
func defer7() int { a := 2 defer func() { a = a + 2 }() return a } // 控制台输出:2
func add(i *int) { *i = *i + 2 } func defer8() int { a := 2 defer add(&a) return a } // 控制台输出:2
原理:
例如:return a,此行代码通过编译后,会被拆分为: 1. 返回值 = a 2. 调用 defer 函数 3. return
defer
的影响函数中,值传递
和引用传递
它们的区别是比较简单的,为基础的 C 语言指针知识。
而对于为何 defer 修饰的背包函数,若是函数内部不是使用传参的参数时,它所能起到的引用修改做用。原理以下:
a := 2 func() { fmt.Println(a) }() a = a + 3
// 内存 闭包外: 1. a 实例化 2. a地址 ---> 2 闭包内: 1. a 地址被传递进来 2. a地址 ---> 2 3. a = a + 3 4. 输出 5