为完成某一功能的程序指令(语句)的集合,称为函数。
Go 语言标准库提供了多种可动用的内置的函数。例如,len() 函数可以接受不同类型参数并返回该类型的长度。
函数声明的语法:
func function_name ([parameter_list]) [return_list] {
函数体
}
语法说明:
示例(函数定义):
/* 函数返回两个数的最大值 */
func max(num1, num2 int) int {
/* 声明局部变量 */
var result int
if (num1 > num2) {
result = num1
} else {
result = num2
}
return result
}
通过函数调用来执行函数体中的代码。
调用函数,向函数传递参数值,函数执行后,返回值。
示例(调用函数max):
package main
import "fmt"
func main() {
var a int = 100
var b int = 200
var ret int
/* 调用函数并返回最大值 */
ret = max(a, b)
fmt.Printf( "最大值是 : %d\n", ret )
}
/* 函数返回两个数的最大值 */
func max(num1, num2 int) int {
/* 定义局部变量 */
var result int
if (num1 > num2) {
result = num1
} else {
result = num2
}
return result
}
Go语言预定义了一些函数,无需引用任何包就可以使用他们,下面写出了所有的内建函数:
不定参是指函数的形参个数为不定数量,为了做到这一点,首先需要将函数定义为接受不定数量参数:
package main
import "fmt"
func dynamicParameter(args ...int) int {
sum := 0
for _, arg := range args {
sum += arg
}
return sum
}
func main() {
fmt.Println(dynamicParameter(1, 2, 3, 4))
fmt.Println(dynamicParameter(1, 2, 3, 4, 5))
}
形如...type
格式的数据类型只能作为函数的参数类型存在,而且必须是最后一个参数。
它是一个语法糖。Go编译器会把传入的实际参数打包为一个数组切片传给该形参。数组切片将在后续的课程中介绍。
前面的例子中将不定参的类型指定为int,如果你希望传任意类型,可以指定类型为interface{}。下面是Go语言标准库中fmt.Printf()的函数原型:
func Printf(format stirng, args ...interface{}) {
// ...
}
用interface{}传递任意类型数据是Go语言的管理用法。
示例(MyPrintf):
package main
import "fmt"
func MyPrintf(args ...interface{}) {
for _, arg := range args {
switch arg.(type) {
case int:
fmt.Println(arg, "is an int value.")
case string:
fmt.Println(arg, "is a string value.")
case int64:
fmt.Println(arg, "is an int64 value.")
default:
fmt.Println(arg, "is an unknown type.")
}
}
}
func main() {
var v1 int = 1
var v2 int64 = 234
var v3 string = "hello"
var v4 float32 = 1.234
MyPrintf(v1, v2, v3, v4)
}
Go的函数可以返回多个值,例如:
package main
import "fmt"
func swap(x, y string) (string, string) {
return y, x
}
func main() {
a, b := swap("Hello", "World")
fmt.Println(a, b) // 输出 World Hello
}
Go函数的返回值支持命名,命名以后的返回值相当于函数内的局部变量,而且Go语言会使用零值对他们进行初始化。
func cal(n1 int, n2 int) (sum int, sub int) {
sum = n1 + n2
sub = n1 - n2
return // 函数会返回sum和sub
}
定义函数时如果使用参数,那么参数列表中的变量称作形参。
形参就像定义在函数体内部的局部变量。
调用函数时,可以通过两种方式来传递参数:
传递类型 | 描述 |
---|---|
值传递 | 值传递是指在调用函数时将实际参数复制一份传递到函数中,这样在函数中如果对参数进行修改,将不会影响到实际参数。 |
引用传递 | 引用传递是指在调用函数时将实际参数的地址传递到函数中,那么在函数中对参数所进行的修改,将影响到实际参数。 |
传递是指在调用函数时将实际参数复制一份传递到函数中,这样在函数中如果对参数进行修改,将不会影响到实际参数。
默认情况下,Go 语言使用的是值传递,即在调用过程中不会影响到实际参数。
以下定义了swap()函数:
/* 定义相互交换值的函数 */
func swap(x, y int) int {
var temp int
temp = x /* 保存 x 的值 */
x = y /* 将 y 值赋给 x */
y = temp /* 将 temp 值赋给 y*/
return temp;
}
接下来,让我们使用值传递来调用swap()函数:
package main
import "fmt"
func main() {
/* 定义局部变量 */
var a int = 100
var b int = 200
fmt.Printf("交换前 a 的值为 : %d\n", a )
fmt.Printf("交换前 b 的值为 : %d\n", b )
/* 通过调用函数来交换值 */
swap(a, b)
fmt.Printf("交换后 a 的值 : %d\n", a )
fmt.Printf("交换后 b 的值 : %d\n", b )
}
/* 定义相互交换值的函数 */
func swap(x, y int) int {
var temp int
temp = x /* 保存 x 的值 */
x = y /* 将 y 值赋给 x */
y = temp /* 将 temp 值赋给 y*/
return temp;
}
执行结果为:
交换前 a 的值为 : 100
交换前 b 的值为 : 200
交换后 a 的值 : 100
交换后 b 的值 : 200
程序中使用的是值传递, 所以两个值并没有实现交互,我们可以使用后面介绍的引用传递来实现交换效果。
指针传递是指在调用函数时将实际参数的地址传递到函数中,那么在函数中对参数所进行的修改,将影响到实际参数。
以下是交换函数 swap() ,使用了指针传递:
/* 定义交换值函数*/
func swap(x *int, y *int) {
var temp int
temp = *x /* 保持 x 地址上的值 */
*x = *y /* 将 y 值赋给 x */
*y = temp /* 将 temp 值赋给 y */
}
以下我们使用指针传递来调用 swap() 函数:
package main
import "fmt"
func main() {
/* 定义局部变量 */
var a int = 100
var b int= 200
fmt.Printf("交换前,a 的值 : %d\n", a )
fmt.Printf("交换前,b 的值 : %d\n", b )
/* 调用 swap() 函数
* &a 指向 a 指针,a 变量的地址
* &b 指向 b 指针,b 变量的地址
*/
swap(&a, &b)
fmt.Printf("交换后,a 的值 : %d\n", a )
fmt.Printf("交换后,b 的值 : %d\n", b )
}
func swap(x *int, y *int) {
var temp int
temp = *x /* 保存 x 地址上的值 */
*x = *y /* 将 y 值赋给 x */
*y = temp /* 将 temp 值赋给 y */
}
以上代码执行结果为:
交换前,a 的值 : 100
交换前,b 的值 : 200
交换后,a 的值 : 200
交换后,b 的值 : 100
定义函数时没有指定函数名。有两种形式:
在定义匿名函数的时候就直接调用:
func main() {
res := func(n1 int, n2 int) int {
return n1 + n2
}(10, 20)
fmt.Println(res)
}
将匿名函数赋值给某个变量,再通过变量名来调用该函数:
func main() {
a := func(n1 int, n2 int) int {
return n1 - n2
}
res := a(10, 30)
}
闭包的概念:闭包就是一个函数和与该函数相关的引用环境组合而成的一个实体。
Go语言的匿名函数可用来实现闭包:当匿名函数中引用了该函数外部所定义的变量就成为了闭包。
以下实例中,我们创建了函数 getSequence() ,返回另外一个函数。该函数的目的是在闭包中递增 i 变量,代码如下:
package main
import "fmt"
func getSequence() func() int {
i:=0
return func() int {
i+=1
return i
}
}
func main(){
/* nextNumber 为一个函数,函数 i 为 0 */
nextNumber := getSequence()
/* 调用 nextNumber 函数,i 变量自增 1 并返回 */
fmt.Println(nextNumber())
fmt.Println(nextNumber())
fmt.Println(nextNumber())
/* 创建新的函数 nextNumber1,并查看结果 */
nextNumber1 := getSequence()
fmt.Println(nextNumber1())
fmt.Println(nextNumber1())
}
以上代码执行结果为:
1
2
3
1
2
示例2:
package main
import "fmt"
func getSequence() (func() int, func() int) {
i := 0
return func() int {
i += 1
return i
}, func() int {
i += 2
return i
}
}
func main() {
nextNumber1, nextNumber2 := getSequence()
fmt.Println(nextNumber1())
fmt.Println(nextNumber2())
fmt.Println(nextNumber1())
fmt.Println(nextNumber2())
fmt.Println(nextNumber1())
fmt.Println(nextNumber2())
}
// 执行结果
// 1
// 3
// 4
// 6
// 7
// 9
Go语言通过defer语句向函数添加结束时执行的代码,常用于释放某些已分配的资源、关闭数据库连接、断开socket、解锁一个加锁的资源等。Go语言保证函数结束后一定会执行defer语句中的代码。
defer语句在函数return语句之后、返回调用者函数之前执行。下面是一个示例:
package main
import "fmt"
func main() {
myFunction()
fmt.Println("after myFunction")
}
func myFunction() {
fmt.Println("enter myFuntion")
defer func() {
fmt.Println("in defer")
}()
fmt.Println("myFunction return")
return
}
// 以上程序输出
// enter myFuntion
// myFunction return
// in defer
// after myFunction
当出现多条defer语句时,按照他们添加的顺序逆序执行(后进先出模式),示例:
func main() {
myFunction()
}
func myFunction() {
for i:=0; i < 5; i++ {
defer fmt.Printf("%d ", i)
}
}
// 以上程序输出
// 4 3 2 1 0
Copyright@2022 , [email protected]