函数声明与调用
本教程共 80 篇 · 第 22 篇 · 更新于 2026-07-27 · 约 8 分钟阅读
22. 函数声明与调用
本节目标:掌握 Go 函数的声明和调用,理解参数传递、多返回值、命名返回值的用法。
函数是一等公民
在 Go 里,函数是一等公民(First-class citizen)。这意味着函数可以:
- 赋值给变量
- 作为参数传递
- 作为返回值返回
- 存储在数据结构里
函数是 Go 程序最核心的组成部分。
函数声明
基本格式:
func 函数名(参数列表) 返回值 {
函数体
}
来看一个完整的例子:
func add(a int, b int) int {
return a + b
}
func是声明函数的关键字add是函数名a int, b int是参数列表int是返回值类型return a + b是函数体
调用函数
result := add(3, 4)
fmt.Println(result) // 7
参数
同类型参数简写
相邻的同类型参数可以只写一次类型:
func add(a, b int) int {
return a + b
}
等价于 func add(a int, b int) int。
参数是值传递
Go 的参数传递是值传递(Pass by value)。函数收到的是参数的副本,修改不影响原值:
func modify(x int) {
x = 100
}
func main() {
n := 10
modify(n)
fmt.Println(n) // 10,没有被修改
}
如果你想在函数里修改外部变量,需要传指针:
func modify(x *int) {
*x = 100
}
func main() {
n := 10
modify(&n)
fmt.Println(n) // 100
}
Note切片、map、channel 虽然也是值传递,但它们底层包含指针,所以看起来像引用传递。修改切片或 map 的元素会影响原数据,但重新赋值不会。
返回值
无返回值
func greet(name string) {
fmt.Println("Hello,", name)
}
不需要写返回值类型,也不需要 return(除非想提前返回)。
单个返回值
func square(x int) int {
return x * x
}
多返回值
Go 支持多返回值,这是 Go 的特色。最常见的用法是返回值和错误:
func divide(a, b float64) (float64, error) {
if b == 0 {
return 0, errors.New("除数不能为零")
}
return a / b, nil
}
func main() {
result, err := divide(10, 3)
if err != nil {
fmt.Println("出错:", err)
return
}
fmt.Println(result) // 3.3333...
}
多返回值用括号括起来,调用时用多个变量接收。
忽略某个返回值
用 _ 忽略不需要的返回值:
result, _ := divide(10, 3) // 忽略错误(不推荐)
_, err := divide(10, 3) // 只取错误
Warning忽略错误是不好的习惯。在实际开发中,应该检查每个 error。
命名返回值
Go 允许给返回值起名字,叫做命名返回值:
func sum(a, b int) (result int) {
result = a + b
return // 裸 return,自动返回 result
}
命名返回值在函数开始时就被声明为零值。你可以在函数体里直接使用它,return 时不需要显式写出返回的变量。
多个命名返回值
func calculate(a, b int) (sum, product int) {
sum = a + b
product = a * b
return
}
func main() {
s, p := calculate(3, 4)
fmt.Println(s, p) // 7 12
}
命名返回值的文档作用
命名返回值可以作为文档,让调用者知道每个返回值的含义:
func getUser(id int) (name string, age int, err error) {
// ...
}
调用者一看就知道第一个返回值是名字,第二个是年龄,第三个是错误。
return 覆盖命名返回值
如果 return 后面显式写了值,会覆盖命名返回值:
func compute(a, b int) (result int) {
result = a + b
return a * b // 返回 a*b,不是 result
}
Tip命名返回值适合短函数。函数太长时,裸
return会让读者困惑返回的是什么值,建议显式写return result。
Go 不支持函数重载
Go 不支持同名函数重载:
func add(a, b int) int {
return a + b
}
func add(a, b float64) float64 { // 报错:add redeclared
return a + b
}
Go 的设计理念是:如果签名不同,就是不同的函数,应该取不同的名字。
需要处理不同类型时,有两个选择:
- 起不同的名字:
func addInt(a, b int) int { return a + b }
func addFloat(a, b float64) float64 { return a + b }
- 用泛型(Go 1.18+):
func add[T int | float64](a, b T) T {
return a + b
}
函数作为值
函数可以赋值给变量,也可以作为参数和返回值:
func main() {
// 赋值给变量
f := add
fmt.Println(f(3, 4)) // 7
// 作为参数
result := apply(add, 3, 4)
fmt.Println(result) // 7
}
func add(a, b int) int {
return a + b
}
func apply(f func(int, int) int, a, b int) int {
return f(a, b)
}
函数类型写法:func(参数类型) 返回值类型。比如 func(int, int) int 表示接收两个 int 返回一个 int 的函数。
函数类型
可以定义自己的函数类型:
type MathFunc func(int, int) int
func apply(f MathFunc, a, b int) int {
return f(a, b)
}
func main() {
var add MathFunc = func(a, b int) int {
return a + b
}
fmt.Println(apply(add, 3, 4)) // 7
}
init 函数
Go 有一个特殊的函数叫 init,它在包被导入时自动执行,不需要手动调用:
package main
import "fmt"
func init() {
fmt.Println("初始化")
}
func main() {
fmt.Println("主函数")
}
输出:
初始化
主函数
init 函数特点:
- 没有参数和返回值
- 一个文件可以有多个
init,按声明顺序执行 - 不能被手动调用
- 主要用于初始化全局变量、注册驱动等
Note执行顺序:包级变量初始化 ->
init函数 ->main函数。
实际示例
计算器
func calculate(op string, a, b float64) (float64, error) {
switch op {
case "+":
return a + b, nil
case "-":
return a - b, nil
case "*":
return a * b, nil
case "/":
if b == 0 {
return 0, errors.New("除数不能为零")
}
return a / b, nil
default:
return 0, fmt.Errorf("不支持的操作:%s", op)
}
}
func main() {
result, err := calculate("/", 10, 0)
if err != nil {
fmt.Println("错误:", err)
return
}
fmt.Println("结果:", result)
}
多返回值交换变量
func swap(a, b int) (int, int) {
return b, a
}
func main() {
x, y := 1, 2
x, y = swap(x, y)
fmt.Println(x, y) // 2 1
}
当然,Go 直接支持多重赋值,不需要函数:
x, y = y, x // 直接交换
常见问题
忘记处理 error
result, _ := divide(10, 0) // 忽略错误
fmt.Println(result) // 输出 0,但不知道出错了
养成习惯:每个返回 error 的函数都要检查。
命名返回值误用
func bad() (result int) {
if cond {
result = 10
// 忘了 return,继续执行
}
// result 还是零值
return // 返回 0 而不是 10
}
命名返回值的裸 return 容易出这种 bug。在复杂逻辑中,显式 return result 更安全。
参数太多
如果函数有超过 5 个参数,考虑用结构体打包:
// 不推荐
func createUser(name string, age int, email string, phone string, city string, country string) {}
// 推荐
type UserConfig struct {
Name string
Age int
Email string
Phone string
City string
Country string
}
func createUser(cfg UserConfig) {}
学完这节你能做什么
- 声明和调用函数
- 使用多返回值处理错误
- 用命名返回值提高可读性
- 把函数作为值传递
- 知道 Go 不支持函数重载
下一节讲变参函数。