多返回值
Go 函数可以返回多个值
匿名返回
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
package main
import "fmt"
func swap(a, b int) (int, int) {
return b, a
}
func main() {
var x, y = swap(10, 20)
fmt.Println(x, y)
}
/* 输出
* 20 10
*/
有形参名返回
1
2
3
4
5
6
7
8
9
10
11
12
13
14
package main
import "fmt"
func swap(a, b int) (x int, y int) {
x = b
y = a
return
}
func main() {
var x, y = swap(10, 20)
fmt.Println(x, y)
}
上述返回值类型相同,可以合并
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
package main
import "fmt"
func swap(a, b int) (x, y int) {
x = b
y = a
// 如果不给 x,y 赋值,默认为0
return
}
func main() {
var x, y = swap(10, 20)
fmt.Println(x, y)
}
init 与 main
init 可以在任意包,也可以在同一个包中出现多次,但建议只写一个
main 只能在 package main 中,且该包必须有该函数
这两个函数为保留函数,定义时不能有参数和返回值
Go 程序会自动调用 init() 和 main()
程序执行
程序的初始化和执行都起始于 main 包,同一个包就算被多个包 import 导入也只会导入一次,下图为执行顺序
例子
假设结构如下
1
2
3
4
5
6
hello
-- InitLib1
-- lib1.go
-- InitLib2
-- lib2.go
main.go
内容如下
lib1.go
1
2
3
4
5
6
7
package InitLib1
import "fmt"
func init() {
fmt.Println("lib1 init")
}
lib2.go
1
2
3
4
5
6
7
package InitLib2
import "fmt"
func init() {
fmt.Println("lib2 init")
}
main.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
package main
import (
"fmt"
// 此处下划线为包起别名,不起别名,导入不调用编译不通过
_ "hello/InitLib1"
_ "hello/InitLib2"
)
func init() {
fmt.Println("main init")
}
func main() {
fmt.Println("main")
}
运行结果
1
2
3
4
lib1 init
lib2 init
main init
main
现在将 Lib1 包导入 Lib2,main 代码不变
lib1.go
1
2
3
4
5
6
7
8
9
10
package InitLib1
import (
"fmt"
_ "hello/InitLib2"
)
func init() {
fmt.Println("lib1 init")
}
运行 main 结果
1
2
3
4
lib2 init
lib1 init
main init
main
lib2 只出现一次
调用其他包函数
上例使用 _
作为别名是匿名的,无法调用相应包的方法
在 lib1 中增加函数,lib1.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
package InitLib1
import (
"fmt"
//_ "hello/InitLib2"
)
// 首字母大写才可在其他包调用
func Lib1Test() {
fmt.Println("lib1 test")
}
func init() {
fmt.Println("lib1 init")
}
main.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
package main
import (
"fmt"
// 给包起别名
mylib1 "hello/InitLib1"
_ "hello/InitLib2"
)
func main() {
// 通过别名.方法调用
mylib1.Lib1Test()
fmt.Println("main")
}
/*
* 输出
* lib1 init
* lib2 init
* lib1 test
* main
*/
或者可以直接使用 .
,文件 main.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
package main
import (
"fmt"
// 别名改为 .
. "hello/InitLib1"
_ "hello/InitLib2"
)
func main() {
// 直接使用
Lib1Test()
fmt.Println("main")
}
不推荐使用,假设俩包有同名函数,将发生歧义
指针
与 C 指针类似
调用函数可以使用两种方式传递参数,值传递与指针 (引用传递) 。默认情况使用值传递,像本文第一段代码即为值传递
使用 &
可以获取变量对应的内存地址
1
2
3
4
5
6
7
8
9
10
package main
import (
"fmt"
)
func main() {
var a int
fmt.Printf("%x", &a)
}
引用传递将内存地址传递给函数,函数修改将影响实际参数,同样是交换函数,这次使用指针
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
package main
import "fmt"
func swap(a, b *int) {
var tmp = *a
*a = *b
*b = tmp
}
func main() {
x, y := 10, 20
swap(&x, &y)
fmt.Println("x =", x, "y =", y)
}
defer
defer 语句用于预定对一个函数的调用,可以称为延迟函数,作用:
- 释放占用的资源
- 捕捉处理异常
- 输出日志
类似于 try…catch…finally 的 finally
常用于处理成对的操作,如打开/关闭文件、获取/释放锁、连接/断开连接等,确保资源被适当地释放,即使在发生错误或提前返回的情况下也能保证执行
如果一个函数中有多个 defer 语句,类似于栈,以 LIFO(后进先出) 顺序执行
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
package main
import "fmt"
func deferDemo() {
defer fmt.Println("1")
defer fmt.Println("2")
defer fmt.Println("3")
defer fmt.Println("4")
}
func main() {
deferDemo()
}
/*
* 输出
* 4
* 3
* 2
* 1
*/
recover
运行时 panic 异常一旦被引发就会导致程序崩溃,recover 为用于 “拦截” 运行时 panic 的内建函数,类似 Java 的 try…catch 的抓取异常
recover 只有在 defer 调用的函数中有效
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
package main
import "fmt"
func deferDemo(i int) {
var arr [10]int
// 错误拦截在错误前设置
defer func() {
// 设置recover拦截错误信息
err := recover()
if err != nil {
fmt.Println(err)
}
}()
arr[i] = 10
}
func main() {
deferDemo(10)
fmt.Println("main code")
}
/*
* 输出
* runtime error: index out of range [10] with length 10
* main code
*/