一架梯子,一头程序猿,仰望星空!
Golang程序设计教程(2024版) > 内容正文

Go语言函数定义与调用完全指南


1 函数基础

在编程中,函数是一段完成特定任务的代码块,可以带有输入参数和返回值。在Go语言中,函数被广泛用来组织和复用代码。有效的函数使用可以让代码更加简洁、可读性强,并且易于维护。

函数是Go语言的核心组成部分,理解如何声明和定义函数对于编写高效且可读的代码至关重要。

2 定义函数

2.1 函数的声明

在Go语言中,函数声明的一般形式是:

func functionName(parameters) returnType {
    // 函数体
}

让我们逐一了解这些组件:

  1. func 关键字是用来声明一个函数。
  2. functionName 是函数的名字,它遵循Go语言的命名规则,即首字母大写的函数是可导出的(Exported),即在包外可见;首字母小写的函数则不可导出,即只能在同一个包内使用。
  3. parameters 是函数接收的参数列表,参数与参数之间用逗号分隔。每个参数后面需要指定它的类型,如果多个参数类型相同,可以只写一次类型。
  4. returnType 是函数返回值的类型。如果函数不返回值,则该部分可以省略。如果函数返回多个值,需要用括号将类型包围起来。

例如,我们可以声明一个简单的函数来计算两个整数的和:

func Add(a int, b int) int {
    return a + b
}

在这个例子中,函数名为 Add,接受两个整数类型的参数 ab,并返回它们的和,返回值类型是 int

2.2 参数列表

参数列表定义了函数接收哪些参数以及每个参数的类型。参数是一种特殊的变量,用于将数据传递给函数。

func Greet(name string, age int) {
    fmt.Printf("Hello, %s! You are %d years old.\n", name, age)
}

在这个函数 Greet 中,nameage 是参数,其中 name 的类型是 stringage 的类型是 int。当调用这个函数时,必须为这两个参数提供实际的值。

2.3 返回类型

函数可以返回计算的结果,返回类型定义了函数返回值的数据类型。函数可以没有返回值,也可以有一个或多个返回值。

若一个函数有多个返回值,则在声明时将它们的类型放在括号内:

func Divide(dividend, divisor float64) (float64, error) {
    if divisor ==  {
        return , errors.New("cannot divide by zero")
    }
    return dividend / divisor, nil
}

这里的 Divide 函数返回两个值:商的结果和一个错误信息。若除数为零,则返回错误。

2.4 可变参数

在Golang中,当我们在函数定义时不确定调用者会传递多少个参数时,可以使用可变参数。可变参数是使用省略号...标识的,它表示函数可以接受任意数量的参数。这在处理不确定数量的数据或实现某些类型的函数(例如格式化、汇总或迭代等功能)时非常有用。

应用场景

可变参数通常用于以下几种情况:

  1. 字符串拼接:如fmt.Sprintf以及strings.Join等函数。
  2. 数组/切片处理:在处理不定长度的数组或切片时,如计算总和、连接多个切片等。
  3. 日志和错误处理:在处理多个错误或记录多个日志信息时,如log.Printf或自定义错误汇总函数。
  4. 辅助函数:用于API或库中,提供给用户更灵活的函数调用方式。

使用可变参数

一个简单的使用可变参数的示例如下:

// 定义一个接受可变整数参数的函数,返回它们的和
func Sum(nums ...int) int {
    total := 0
    for _, num := range nums {
        total += num
    }
    return total
}

func main() {
    // 你可以传入任意数量的参数
    fmt.Println(Sum(1, 2))          // 输出: 3
    fmt.Println(Sum(1, 2, 3, 4))    // 输出: 10
    
    // 也可以传入一个切片,并在切片后加上省略号
    numbers := []int{1, 2, 3, 4, 5}
    fmt.Println(Sum(numbers...))    // 输出: 15
}

Sum函数中,nums是一个整型切片,其内部包含了所有传递给Sum函数的参数。我们可以使用范围for循环来遍历这个切片,并求和。

需要注意,可变参数在函数体内部实际上是一个切片,因此可以使用所有切片相关的操作。当需要将切片作为可变参数传递给函数时,切片后加上省略号...即可。

使用可变参数可以让函数的调用变得更加灵活并且简洁,但它也会对性能产生小幅的影响,因为使用可变参数会涉及到切片的创建和内存分配。因此在对性能有严格要求的情况下要谨慎使用。

提示:关于切片在后续章节会详细讲解,如果你学习过其他编程语言,你可以暂时把他当做数组。

3 调用函数

3.1 基本函数调用

调用函数意味着执行函数的代码。在Go中,调用一个已定义的函数非常直接,只需要使用函数名并传入适当的参数。如:

result := add(3, 4)
fmt.Println(result)  // 输出7

在这个例子中,add函数被调用并且传入了两个整数作为参数,然后将返回的结果赋值给了result变量。

3.2 传递参数

当传递参数给函数时,Go默认使用值传递,这意味着传递参数值的副本,原始数据不会被改变。但是,如果你希望函数能够修改外部变量,或者是为了性能考虑,你可以使用引用传递,比如指针或者传递大型的结构体。

下面是值传递和引用传递的例子:

// 值传递示例
func double(val int) {
    val *= 2
}

// 引用传递示例
func doublePtr(val *int) {
    *val *= 2
}

func main() {
    value := 3
    double(value)
    fmt.Println(value)  // 输出3,value不变

    doublePtr(&value)
    fmt.Println(value)  // 输出6,value翻倍
}

在上面的例子中,double函数尝试去翻倍传入的val,但是它只会翻倍它的副本,原始value变量保持不变。然而,doublePtr函数通过接收一个整型变量的指针作为参数来改变原始变量的值。