go语言中一定有一个main()函数,作为程序的入口,这个跟C/C++是一样的,有main()函数的package是main package,go语言通过package(包)来组织程序,跟C/C++中引用头文件不同,要清晰很多,C/C++在编译时不会检测引入的头文件是否有用,而引入过多的头文件会导致编译起来特别慢。go编译时会检查引入的包是否用了,如果没有用会报错,这一点就非常好的。
go中的函数用关键字func开头,形式是
func 函数名(参数列表) 返回值类型 {
//todo
}
函数名根据约定,函数名首字母大写即为public,小写即为private。其实我们通过调用go提供包的函数时可以看出,我们调用的函数首字母都是大写的,比如(fmt.Println)。
go语言中的函数支持多个返回值,这个是C/C++中没有的,在写C/C++程序的时候,经常遇到要返回多个数值的时候,因为不支持,只能通过引用参数带出函数中的计算数值,感觉不是特别方便。
示例代码如下:
package main
import (
"fmt"
"strconv"
)
//这种是值传递,将实际参数复制一份传递到函数中,在函数中如果对参数进行修改,将不会影响到实际参数
func addVal1(v1, v2 int, str string) (int, string) {
r1 := v1 + v2
r2 := str + "_" + strconv.Itoa(r1)
return r1, r2 //返回一个int和一个string
}
func addVal2(v1, v2 int, str string) (r1 int, r2 string) { //可以给返回值起一个变量名,go推荐写法
r1 = v1 + v2
r2 = str + "_" + strconv.Itoa(r1)
return //如果有返回值必须在函数内部添加return
}
//不定参数类型,...type
func addVal3(str string, args ...int) (int, string) { //不定参数个数可以是0或者多个
var r1 int
for i := 0; i < len(args); i++ { //len(args)获取用户传递参数个数
r1 += args[i]
}
r2 := str + "_" + strconv.Itoa(r1)
return r1, r2 //
/*
这种方式也可以
for i, data := range args{
}*/
}
//函数递归实现累加
func sumVal(i int) int {
if i == 1 {
return i
}
return i + sumVal(i-1)
}
func main() {
r1, r2 := addVal2(100, 1, "addVal1")
fmt.Println(r2, r1)
r3, r4 := addVal2(100, 1, "addVal2")
fmt.Println(r4, r3)
r5, r6 := addVal3("addVal2", 100, 1, 10)
fmt.Println(r6, r5)
sum := sumVal(10)
fmt.Println(sum)
}
go语言支持匿名函数,可以叫做闭包,匿名函数是一个“内联”语句或者表达式,可以直接使用函数内的变量(以引用方式捕获外部变量,可以修改外部变量)。
package main
import "fmt"
func test() func() int { //返回值是一个无参数返回值为int的函数
i := 0
return func() int {
i++
return i
}
}
func main() {
i := 100
str := "hello"
//匿名函数 后面加()直接调用
func() { //以引用方式捕获外部变量,可以修改
i = 55
fmt.Println(str, i)
}()
f1 := func() { //:=自动推导类型 f1 为函数类型变量
i = 66
fmt.Println(str, i)
}
//调用
f1()
fmt.Println(str, i)
/* f 是一个函数 其中变量i=0
test函数返回值是一个匿名函数
*/
f := test()
/*
通过f来调用返回的匿名函数,闭包不关心捕获的的变量已经超出作区域,
只要还在使用它,这些变量就一直存在,所以i初始值0 每次调用+1
*/
fmt.Println(f()) //1
fmt.Println(f()) //2
fmt.Println(f()) //3
fmt.Println(f()) //4
fmt.Println(f()) //5
f = test()
fmt.Println(f()) //1
fmt.Println(f()) //2
}
defer关键字作用是后面跟随的语句延迟调用,会在defer归属的函数结束之前调用。它一般用于释放某些已经申请的资源(比如互斥锁,或者打开的文件)。如果是多个defer遵循LIFO(先进后出)出栈的方式处理。哪怕函数执行过程中或者defer语句出现错误,后面执行的defer的语句依然会被执行。
示例代码如下:
package main
import "fmt"
func main() {
defer fmt.Println("first defer")
defer fmt.Println("second defer")
var x int
r := 100 / x
defer fmt.Println("last defer", r)
}
package main
import "fmt"
func main() {
x := 1
y := 100
defer func(a, b int) {
fmt.Printf("x = %d y = %d\n", a, b)
}(x, y) //代表调用此匿名函数,并且把参数传递进去了,只是没有调用
x = 2
y = 200
fmt.Printf("x = %d y = %d\n", x, y)
}