引入

有时候调用函数不确定要传几个参数——可能是 2 个,也可能是 20 个。Go 用 语法支持可变参数,函数内部把它当切片处理,调用时既可以逐个传参,也可以把切片展开传进去。

正文

定义

定义

可变参数允许函数接收任意数量的同类型参数。Go 用 标记可变参数,函数内部以切片的方式访问这些参数。调用时可以用切片加 展开传入。

语法

// 声明可变参数
func 函数名(参数 ...类型) {
    // 参数在函数内是切片
}
 
// 调用时逐个传参
函数名(1, 2, 3)
 
// 调用时展开切片
nums := []int{1, 2, 3}
函数名(nums...)

例子

基本可变参数

func sum(nums ...int) int {
    total := 0
    for _, n := range nums {
        total += n
    }
    return total
}
 
func main() {
    fmt.Println(sum(1, 2, 3))       // 6
    fmt.Println(sum(10, 20))         // 30
    fmt.Println(sum())               // 0
}

nums 在函数内部是 []int 类型,不传参数时是空切片。

固定参数 + 可变参数

func greet(prefix string, names ...string) {
    for _, name := range names {
        fmt.Println(prefix, name)
    }
}
 
func main() {
    greet("Hello,", "Alice", "Bob", "Carol")
    // Hello, Alice
    // Hello, Bob
    // Hello, Carol
}

可变参数只能放在最后一个,前面可以有固定参数。

切片展开传入

func sum(nums ...int) int {
    total := 0
    for _, n := range nums {
        total += n
    }
    return total
}
 
func main() {
    nums := []int{1, 2, 3, 4, 5}
    result := sum(nums...)
    fmt.Println(result) // 15
}

已有的切片用 展开后传入,Go 不会创建新切片,直接复用原来的。

配合 fmt.Printf

func main() {
    fmt.Println("a", "b", "c")
    fmt.Printf("%d %d %d\n", 1, 2, 3)
}

fmt.Printlnfmt.Printf 本身就用了可变参数,所以能接收任意数量的值。

可变参数传递

func log(format string, args ...interface{}) {
    fmt.Printf(format+"\n", args...)
}
 
func main() {
    log("name: %s, age: %d", "Alice", 25)
}

…interface{} 可以接收任意类型的参数,在函数内部再展开传给其他可变参数函数。

常见写法

写法说明
func f(nums …int)可变参数声明
f(1, 2, 3)逐个传参调用
f(slice…)切片展开传入
func f(prefix string, nums …int)固定参数 + 可变参数
func f(args …interface{})接收任意类型参数

特点

  • 可变参数用 声明,必须是最后一个参数
  • 函数内部把可变参数当切片使用,可以 range 遍历
  • 不传可变参数时,切片为 nil(不是空切片)
  • 切片展开用 slice…不会创建副本
  • …interface{} 可以接收任意类型的参数
  • 一个函数只能有一个可变参数

理解

可变参数让函数的参数数量变得灵活,不用为了不同数量的输入写一堆重载。核心就是 在声明端表示”不定长”,在调用端表示”展开”。记住可变参数在函数内就是切片,所有切片操作都能直接用。

引出

函数学完了,接下来看一个和函数紧密相关的语句——defer。它延迟执行的特性在函数返回前才执行,是资源释放的好帮手。看看 Go defer 怎么用。