文章目录
6.1 函数
6.1.1 函数的基本语法
func 函数名 (形参列表) (返回值列表){
执行语句...
return 返回值列表
}
-
形参列表:表示函数的输入
-
函数中的语句:表示为了实现某一功能代码块
-
函数可以有返回值,也可以没有
6.1.2 函数使用的注意实现和细节讨论
-
函数的形参列表可以是多个,返回值列表也可以是多个。
-
形参列表和返回值列表的数据类型可以是值类型和引用类型。
-
函数的命名遵循标识符命名规范,首字母不能是数字。首字母大写该函数可以被本包文件和其他包文件使用;首字母小写只能被本包文件使用,其它包不能使用。
-
函数中的变量是局部的,函数外不生效。
-
基本数据类型和数组是默认值传递的,即进行值拷贝。在函数内修改不会影响到原来的值。
-
如果希望函数内的变量能修改函数外的变量(指默认以值传递的方式传递的数据类型),可以传入变量的地址&,函数内以指针的方式操作变量,从效果上看类似引用。
-
Go函数不支持函数重载。
-
在Go中,函数也是一种数据类型,可以赋值给一个变量,则该变量就是一个函数类型的变量,通过该变量对函数调用。 因此,函数也可以作为形参并且调用。
-
为了简化数据类型定义,Go支持自定义数据类型。
type 自定义数据类型名 数据类型
。 -
支持对函数返回值命名,如:
a, b = myFunction(c,d)
,a、b就是对返回值命名。 -
使用"_"标识符,忽略返回值。
-
Go支持可变参数,可变参数需要放在形参列表最后。
//args是slice切片,通过args[index]可以访问到各个值
//支持0到多个参数
func sum(args... int) sum int {
}
//支持1到多个参数
func sum(n1 int, args... int) sum int {
}
//例子
func sum(n1 int, args... int) int {
sum := n1
for i := 0; i < len(args); i++ {
sum += args[i]
}
return sum
}
res := sum(10, -1, 2, 90, 0) // 10是n1,-1开始是args
fmt.Println("res=", res)
6.1.3 init函数
每一个源文件都可以包含一个init函数,该函数会在main函数执行前被Go运行框架调用。
-
注意事项
-
如果一个文件同时包含全局变量定义、init函数和main函数,则执行的流程:全局变量定义—init函数—main函数。
-
如果main包(全局变量定义、init函数、main函数)引用了go包(全局变量定义、init函数),且执行的流程:go全局变量定义—go init函数—main全局变量定义—main init函数—main main函数。
-
6.1.4 匿名函数
Go支持匿名函数,匿名函数就是没有名字的函数,如果我们某个函数只希望使用一次,可以考虑使用匿名函数,匿名函数也可以实现多次调用。
- 使用方式1(只能调用一次):
res := func (n1 int, n2 int) int {
return n1 + n2
} ( 10, 20)
fmt.Println("res = ", res) //30
- 使用方式2(将匿名函数赋给一个变量,再通过这个变量来调用匿名函数):
a := func (n1 int, n2 int) int {
return n1 - n2
}
res = a(10, 30)
fmt.Println("res = ", res) //-20
- 全局匿名函数
如果将匿名函数赋给一个全局变量,那么这个匿名函数就成为一个全局匿名函数,可以在程序有效。
6.1.5 函数的defer
在函数中,程序员经常需要创建资源(比如:数据库连接、文件句柄、锁等),为了在函数执行完毕后,及时地释放资源,Go的设计者提供defer。
package main
import "fmt"
func sum(n1 int, n2 int) int {
//当执行到defer时,会将defer后面的语句压入到独立的栈,暂时不执行
//当函数执行完毕后,再从栈中按照先进后出的方式执行(即栈的特性)
defer fmt.Println("ok1 n1 = ", n1) //执行3
defer fmt.Println("ok2 n2 = ", n2) //执行2
res := n1 + n2
fmt.Println("ok3 res = ", res) //执行1
return res
}
func main() {
res := sum(10, 20)
fmt.Println("res = ", res) //执行4
}
-
defer的注意事项和细节:
- 当go执行到一个defer时,不会立即执行defer后的语句,而是将defer后的语句压入到一个栈中,然后继续执行函数下一语句。
-
当函数执行完毕后,再从defer中依次从栈顶取出语句执行(遵循栈先进后出的原则)。
-
在defer将语句放入栈时,也会将相关的值拷贝同时入栈。
package main
import "fmt"
func sum(n1 int, n2 int) int {
//当执行到defer时,会将defer后面的语句压入到独立的栈,暂时不执行
//当函数执行完毕后,再从栈中按照先进后出的方式执行(即栈的特性)
defer fmt.Println("ok1 n1 = ", n1) //执行3
defer fmt.Println("ok2 n2 = ", n2) //执行2
n1++ //这句话不会影响栈中存放的n1值,仍然为10
n2++ //同理,栈中的n2仍然为20
res := n1 + n2
fmt.Println("ok3 res = ", res) //执行1
return res
}
func main() {
res := sum(10, 20)
fmt.Println("res = ", res) //执行4
}
-
defer的最佳实践:defer最重要的价值在于当函数执行完毕后,可以及时的释放函数创建的资源。
-
在Go编程中的通常做法是,创建资源后(如打开了文件、获取了数据库的连接、或者锁资源),可以执行
defer file.Close()
或defer connect.Close()
-
在defer后,可以继续使用创建资源
-
当函数完毕后,系统会依次从栈中,取出语句关闭资源
-
//模拟代码如下
func test(){
file = openfile(文件名)
defer file.close()
//其他代码
connect = ope