基本语法
Go中函数的基本语法如下:

使用案例如下:
package main
import "fmt"
// 函数定义
func Factorial(num int) int {
result := 1
for i := 2; i <= num; i++ {
result *= i
}
return result
}
func main() {
result := Factorial(10) // 函数调用
fmt.Printf("result = %d\n", result) // result = 3628800
}
注意: Go中的函数不支持函数重载。
参数传递方式
参数传递的方式有两种:
值传递案例如下:
package main
import "fmt"
func ModifyNum(num int) { // 值传递
num = 20
fmt.Printf("num = %d\n", num) // num = 20
}
func main() {
var num int = 10
ModifyNum(num)
fmt.Printf("num = %d\n", num) // num = 10(原始数据未被影响)
}
对于值类型参数,如果希望函数内部对参数的修改能影响到原始数据,可以传入值类型变量的地址,然后在函数内部通过指针的方式操作变量。如下:
package main
import "fmt"
func ModifyNum(num *int) { // 引用传递
*num = 20
fmt.Printf("num = %d\n", *num) // num = 20
}
func main() {
var num int = 10
ModifyNum(&num)
fmt.Printf("num = %d\n", num) // num = 20(原始数据被影响)
}
返回多个值
Go中函数支持返回多个值,通过返回值列表指明各个返回值的类型即可。如下:
package main
import "fmt"
func GetSumAndSub(num1 int, num2 int) (int, int) { // 返回多个值
sum := num1 + num2
sub := num1 - num2
return sum, sub
}
func main() {
sum, sub := GetSumAndSub(10, 20)
fmt.Printf("sum = %d\n", sum) // sum = 30
fmt.Printf("sub = %d\n", sub) // sub = -10
}
说明一下:
忽略返回值
如果函数返回多个值,在接收时,可以通过_(占位符)忽略不需要的返回值。如下:
package main
import "fmt"
func GetSumAndSub(num1 int, num2 int) (int, int) { // 返回多个值
sum := num1 + num2
sub := num1 - num2
return sum, sub
}
func main() {
_, sub := GetSumAndSub(10, 20)
fmt.Printf("sub = %d\n", sub) // sub = -10
}
返回值命名
Go中函数支持在返回值列表给返回值命名,这时函数在返回时无需在return后指明需要返回的值,可以避免返回顺序出错。如下:
func GetSumAndSub(num1 int, num2 int) (sum int, sub int) { // 返回多个值
sum = num1 + num2
sub = num1 - num2
return
}
可变参数
Go中函数支持可变参数,只需在对应形参类型的前面加上...即可将其设置为可变参数,可变参数支持传入0个或多个参数。如下:
package main
import "fmt"
func Sum(nums ...int) (sum int) { // 可变参数
fmt.Printf("nums type = %T\n", nums) // nums type = []int
for i := 0; i < len(nums); i++ {
sum += nums[i]
}
return
}
func main() {
fmt.Printf("result1 = %d\n", Sum()) // result1 = 0
fmt.Printf("result2 = %d\n", Sum(10, 20)) // result2 = 30
fmt.Printf("result3 = %d\n", Sum(10, 20, 30)) // result3 = 60
}
说明一下:
函数类型
在Go中函数也是一种数据类型,可以将其赋值给一个变量,然后通过该变量即可对函数进行调用。如下:
package main
import "fmt"
func Sum(num1 int, num2 int) int {
return num1 + num2
}
func main() {
sumFunc := Sum
fmt.Printf("sumFunc type = %T\n", sumFunc) // sumFunc type = func(int, int) int
fmt.Printf("sum = %d\n", sumFunc(10, 20)) // sum = 30
}
自定义数据类型
Go中自定义数据类型的基本语法如下:

使用案例如下:
package main
import "fmt"
func main() {
type MyInt int // 自定义数据类型
var num1 MyInt = 10
fmt.Printf("num1 type = %T\n", num1) // num1 type = main.MyInt
var num2 int = int(num1)
fmt.Printf("num2 type = %T\n", num2) // num2 type = int
}
说明一下:
在Go中将函数作为形参也是常见的用法,这时结合自定义数据类型给函数类型取别名,能有效提高代码的可读性。如下:
package main
import "fmt"
type SumType func(int, int) int // 自定义数据类型
func MyFunc(f SumType, num1 int, num2 int) int {
return f(num1, num2)
}
func main() {
result := MyFunc(Sum, 10, 20)
fmt.Printf("result = %d\n", result) // result = 30
}
匿名函数
Go中支持匿名函数,如果希望某个函数只使用一次,可以考虑使用匿名函数。如下:
package main
import "fmt"
func main() {
// 匿名函数
result := func(num1 int, num2 int) int { // 定义匿名函数并调用
return num1 + num2
}(10, 20)
fmt.Printf("result = %d\n", result) // result = 30
}
在定义匿名函数时如果将其赋值给一个变量,后续再通过该变量来调用匿名函数,就能实现匿名函数的多次调用。如下:
package main
import "fmt"
func main() {
// 匿名函数
f := func(num1 int, num2 int) int { // 定义匿名函数
return num1 + num2
}
fmt.Printf("result = %d\n", f(100, 200)) // result = 300
}
defer机制
defer相关介绍:
下面是一个经典的defer题目,请问程序的输出结果是什么。如下:
package main
import "fmt"
func DeferFunc(num1 int, num2 int) {
defer fmt.Printf("defer num1 = %d\n", num1)
defer fmt.Printf("defer num2 = %d\n", num2)
num1++
num2++
fmt.Printf("num1 = %d\n", num1)
fmt.Printf("num2 = %d\n", num2)
}
func main() {
DeferFunc(10, 20)
fmt.Println("main code...")
}
程序运行结果如下:

说明一下:
defer使用案例
下面是一个对文件操作的函数,其中利用defer机制对文件资源进行了延时释放,在函数执行完毕后文件会自动关闭。如下:
func FileOperation(filename string) (err error) {
file, err := os.Open(filename) // 打开文件
if err != nil {
fmt.Printf("open file error, err = %v\n", err)
return
}
defer file.Close() // 关闭文件
// 进行文件操作...
return
}
闭包
闭包相关介绍:
使用案例如下:
package main
import "fmt"
// 创建累加器
func CreateCounter(base int) func(int) int {
var count = base
return func(n int) int { // 返回闭包
count += n
return count
}
}
func main() {
// 闭包
f := CreateCounter(10)
fmt.Printf("count = %d\n", f(1)) // count = 11
fmt.Printf("count = %d\n", f(3)) // count = 14
fmt.Printf("count = %d\n", f(5)) // count = 19
}
说明一下:
闭包的优势
闭包的优势如下:
基本概念
在Go语言中,包是一种组织和封装代码的方式,每一个go文件都归属于一个包,而一个包可以包含多个文件。
包的主要优势如下:
在Go项目中,通常一个包对应一个目录,包名与目录名相同,每个目录下的go文件都归属于当前包。如下:

包的使用
包的使用方式如下:
例如,下面在Go项目中创建了db、utils、main三个目录,分别用于存放db包、utils包和main包的go文件代码,并在main包中调用了db包和utils包中的函数。如下:

说明一下:
init函数
在每一个go文件中都可以包含一个init函数,该函数会在main函数执行前被Go运行框架调用。如下:
package main
import (
"fmt"
)
func init() {
fmt.Println("main init...")
}
func main() {
fmt.Println("main run...")
}
代码的运行结果如下:

说明一下:
init函数的执行顺序
init函数的执行顺序如下:
下面分别在之前的main包、utils包和db包中添加了一些输出代码,方便看到程序的执行顺序。
main.go文件中的代码如下:
package main
import (
"fmt"
"go_code/FunctionAndPackage/PackageDemo/db"
"go_code/FunctionAndPackage/PackageDemo/utils"
)
var globalNum = test()
func init() {
fmt.Println("main init...")
}
func test() int {
fmt.Println("main global variable init...")
return 0
}
func main() {
fmt.Println("main run...")
db.DbPrint()
utils.UtilsPrint()
}
utils.go文件中的代码如下:
package utils
import "fmt"
var globalNum = test()
func init() {
fmt.Println("utils pakcage init...")
}
func test() int {
fmt.Println("utils global variable init...")
return 0
}
func UtilsPrint() {
fmt.Println("utils package print...")
}
db.go文件中的代码如下:
package db
import "fmt"
var globalNum = test()
func init() {
fmt.Println("db pakcage init...")
}
func test() int {
fmt.Println("db global variable init...")
return 0
}
func DbPrint() {
fmt.Println("db package print...")
}
运行程序后可以看到,按照main包中的导入顺序先后对db包和utils包进行了初始化,然后再对main包进行了初始化,每个包在初始化过程中先对全局变量进行了初始化,然后再调用了init函数,在所有包初始化完毕后开始执行main函数的代码逻辑。如下:

说明一下: