Golang语言之函数(func)基础篇

简介: 这篇文章深入讲解了Golang语言中函数的定义和使用,包括函数的引入原因、使用细节、定义语法,并通过多个案例展示了如何定义不返回任何参数、返回一个或多个参数、返回值命名、可变参数的函数,同时探讨了函数默认值传递、指针传递、函数作为变量和参数、自定义数据类型以及返回值为切片类型的函数。

                                              作者:尹正杰
版权声明:原创作品,谢绝转载!否则将追究法律责任。

一.函数的引入

1.为什么要使用函数

函数的作用就是提高代码的复用,减少代码的冗余,代码的维护性也提高了。

为完成某一功能的程序指令(语句)的集合,称为函数。

2.函数使用的细节

- 1.函数是对特定的功能进行提取,形成一个代码块,这个代码块就是我们所说的函数;

- 2.函数的作用就是提高代码的复用性;

- 3.函数和函数是并列的关系,所以我们定义的函数不能写到main函数中; 

- 4.Golang中函数不支持重载(所谓重载就是函数名相同,形参列表不同的情况,Java语言支持重载),如果你强行定义则会报错重复定义;

- 5.Go语言支持对函数返回值命名;

- 6.Golang中支持可变参数,如果你希望函数带有可变的参数;

- 7.基本数据类型和数组默认都是值传递的,即进行值拷贝,在函数内修改,不会影响到原来的值;

- 8.以值传递的数据类型,如果希望在函数内的变量能修改函数外的变量,可以传入变量的指针地址("&"),函数内以指针的方式操作变量,从效果来看类似引用传递;

- 9.在Go中,函数也是一种数据类型,可以赋值给一个变量,则该变量就是一个函数类型的变量了,通过该变量可以对函数调用;

- 10.函数既然是一种数据类型,因此在Go中,函数可以作为形参(把函数本身当做一种数据类型),并且调用;

- 11.为了简化数据类型定义,Go支持此自定义数据类型,比如"type 字典变异数据类型名称 数据类型",可以理解为"起别名";

- 12.当我们的一个函数返回值类型为slice时,nil可以看做是一个有效的slice,没必要显示返回一个长度为0的切片;

3.定义函数语法

Go语言中定义函数使用func关键字,具体格式如下:
    func 函数名(形参列表)(返回值类型列表){
        函数体代码块
        return + 返回值列表
    }


相关参数说明如下:
    函数名:
        - 1.遵循标识符命名规范,由字母、数字、下划线组成。但函数名的第一个字母不能是数字;
        - 2.在同一个包内,函数名也称不能重名(包的概念后续讲解会陆续分享哟~);
        - 3.首字母大写函数可以被本报文件和其他包文件使用(类似JAVA语言的public关键字);
        - 4.首字母小写只能被本报文件使用,其他包文件不能被使用(类似JAVA语言的private关键字);

    形参列表:
        - 1.参数由参数变量和参数变量的类型组成,多个参数之间使用","分隔;
        - 2.形参列表的个数是根据你功能需要设定的,可以不指定形参列表,也可以定义多个形参;
        - 3.形参列表(简称"形参")的作用就是用来接收外来的数据;
        - 4.实际参数(简称:"实参")是实际传入函数的数据;

    返回值类型列表:
        - 1.返回值由返回值变量和其变量类型组成,也可以只写返回值的类型。
        - 2.多个返回值必须用"()"包裹,并用","分隔。
        - 3.返回值个数是根据你功能需要设定的,可以不指定返回值,也可以定义多个返回值;

    函数体:
        实现指定功能的代码块。

二.定义函数案例

1.不返回任何参数

package main

import "fmt"

// 自定义函数功能: 实现两个数相加的和
//   - 1.函数的参数中如果相邻变量的类型相同,则可以省略类型,这两个参数的类型均为int,因此可以省略m的类型,因为n后面有类型说明,m参数也是该类型。
//   - 2.定义的函数可以没有返回值哟
func calculateSum(m, n int) {
    sum := m + n
    fmt.Printf("%d + %d = %d\n", m, n, sum)
}

func main() {

    // // 功能1: 100 + 200
    // var (
    //     a   int = 100
    //     b       = 200
    //     sum     = 0
    // )

    // // sum += a
    // // sum += b
    // sum = calculateSum(a,b)
    // fmt.Printf("%d + %d = %d\n", a, b, sum)

    // // 功能2: 300 + 400
    // var (
    //     x    int = 300
    //     y        = 400
    //     sum2     = 0
    // )

    // sum2 += x
    // sum2 += y

    // fmt.Printf("%d + %d = %d\n", x, y, sum2)

    // 使用函数功能,就可以将上面需要重复写的代码功能提取出来,方便以后多次调用了,提高了代码的冗余性
    var (
        a int = 100
        b     = 200
        x     = 300
        y     = 400
    )

    // 调用函数
    calculateSum(a, b)
    calculateSum(x, y)
}

2.返回一个参数

package main

import "fmt"

// 自定义函数功能: 实现两个数相加的和
//   - 1.函数的参数中如果相邻变量的类型相同,则可以省略类型,这两个参数的类型均为int,因此可以省略m的类型,因为n后面有类型说明,m参数也是该类型。
//   - 2.如果返回值类型就一个的话,那么"()"是可以省略不写的哟;
func calculateSum(m, n int) int {
    return m + n
}

func main() {

    var (
        a int = 500
        b     = 600
        x     = 700
        y     = 800
    )

    // 调用函数
    sum1 := calculateSum(a, b)
    sum2 := calculateSum(x, y)

    fmt.Printf("%d + %d = %d\n", a, b, sum1)
    fmt.Printf("%d + %d = %d\n", x, y, sum2)
}

3.返回多个参数

package main

import "fmt"

// 自定义函数功能: 实现两个数的"和","差"。
//   - 1.函数的参数中如果相邻变量的类型相同,则可以省略类型,这两个参数的类型均为int,因此可以省略m的类型,因为n后面有类型说明,m参数也是该类型。
//   - 2.如果返回值类型有多个的话,那么"()"是不可以省略的;
func calculate(m, n int) (int, int) {
    return m + n, m - n
}

func main() {

    var (
        a int = 500
        b     = 600
        x     = 800
        y     = 900
    )

    // 调用函数
    sum, sub := calculate(a, b)

    fmt.Printf("%d + %d = %d\n%d - %d = %d\n", a, b, sum, a, b, sub)

    // 如果有返回值不想接收,那么可以使用"_"进行忽略,
    sum2, _ := calculate(x, y)
    fmt.Printf("%d + %d = %d\n", x, y, sum2)

}

4.返回值命名

package main

import (
    "fmt"
)

// 函数定义时可以给返回值命名,并在函数体中直接使用这些变量,最后通过return关键字返回。
func calculate(x, y int) (sum, sub int) {
    fmt.Println("in calculate ... 返回值命名")
    sub = x - y
    sum = x + y
    // 注意,此处无需写返回的对象,因为默认会返回sum和sub变量哟~顺序是固定的
    return
}

func calculate2(x, y int) (int, int) {
    fmt.Println("in calculate2 ...")
    sub := x - y
    sum := x + y
    // 定义要返回的顺序,至于需要第一个参数先返回sub还是sum可以由你来定义
    return sub, sum
}

func main() {
    var (
        a int = 100
        b int = 20
    )

    result01, result02 := calculate(a, b)
    fmt.Printf("%d + %d = %d\n", a, b, result01)
    fmt.Printf("%d - %d = %d\n", a, b, result02)

    result03, result04 := calculate2(a, b)
    fmt.Printf("%d + %d = %d\n", a, b, result04)
    fmt.Printf("%d - %d = %d\n", a, b, result03)

}

5.可变参数

package main

import (
    "fmt"
)

// 1.可变参数是指函数的参数数量不固定,Go语言中的可变参数通过在参数名后加“...”来标识。
// 2.可变参数通常要作为函数形参的最后一个参数。
func calculate(operator string, args ...int) int {
    sum := 0
    mul := 1
    if operator == "+" {
        for _, v := range args {
            sum += v
        }
        return sum
    } else if operator == "*" {
        for _, v := range args {
            mul *= v
        }
        return mul
    } else {
        return -1
    }
}

func main() {

    var (
        a int = 10
        b     = 20
        c     = 30
        d     = 40
    )

    sum := calculate("+", a, b, c, d)
    mul := calculate("*", a, b, c)

    fmt.Printf("sum = %d\n", sum)
    fmt.Printf("mul = %d\n", mul)
}

三.函数使用细节

1.函数默认以值传递

package main

import "fmt"

func swapNumber(a, b int) {
    var temp int
    temp = a
    a = b
    b = temp
}

func main() {
    var (
        x int = 100
        y     = 200
    )

    fmt.Printf("交换前的两个数: x = [%d], y = [%d]\n", x, y)
    swapNumber(x, y)
    fmt.Printf("交换后的两个数: x = [%d], y = [%d]\n", x, y)
}

2.函数实现指针传递

package main

import "fmt"

// 参数的类型为指针
func updateValue(number *int) {
    // 对指针地址对应的变量进行修改
    *number = 200
}

func main() {
    var x int = 100
    fmt.Printf("x的地址为:%v, x = [%d]\n", &x, x)

    // 注意,此处我们传递的是变量的地址哟
    updateValue(&x)

    fmt.Printf("x的地址为:%v, x = [%d]\n", &x, x)

}

3.函数作为变量

package main

import "fmt"

func xixi(number int) {
    fmt.Printf("in xixi ...,您传递的数字为: [%d]\n", number)
}

func main() {

    // 在Go中,函数也是一种数据类型,可以赋值给一个变量,则该变量就是一个函数类型的变量了,通过该变量可以对函数调用;
    haha := xixi

    fmt.Printf("xixi的类型是: [%T],haha的类型是: [%T]\n", xixi, haha)

    // 调用haha等同于调用"xixi(100)"
    haha(100)
}

4.函数作为形参

package main

import "fmt"

func xixi(number int) {
    fmt.Println("in xixi函数")
}

// 定义一个函数,把另一个函数作为形参传递
func haha(a int, b float64, testFunc func(int)) {
    fmt.Println("in hehe函数")
}

func main() {

    // 定义一个xixi函数的变量,其函数签名为: "func(int)"
    heihei := xixi

    // 调用haha函数
    haha(100, 3.14, xixi)
    haha(200, 9.18, heihei)
}

5.自定义数据类型

package main

import "fmt"


func xixi(number int) {
    fmt.Println("in xixi函数")
}

// 我们可以给函数起别名
type myFunc func(int)

// 定义一个函数,把另一个函数作为形参传递
func haha(a int, b float64, testFunc myFunc) {
    fmt.Println("in hehe函数")
}


func main() {

    // 为了简化数据类型定义,Go支持此自定义数据类型,此处是给int类型齐了个别名叫myInt类型
    type myInt int

    var a myInt = 100

    var b int = 200

    // myInt类型虽然是int类型的别名,但是在Go中编译识别的时候还是认为myInt和int不是一种数据类型。
    // b = a // 此行编译不通过,报错:"cannot use a (variable of type myInt) as int value in assignment"

    // 如果想要编译通过,则必须将a强制转换为b的类型。
    b = int(a)

    fmt.Printf("a = %d,b=%d\n", a, b)


    heihei := xixi
    haha(100, 3.14, xixi)
    haha(200, 9.18, heihei)
}

6.返回值为切片类型

package main

import (
    "fmt"
)

// 当我们的一个函数返回值类型为slice时,nil可以看做是一个有效的slice,没必要显示返回一个长度为0的切片。
func someFunc(x string) []int {
    if x == "" {
        // 没必要返回"[]int{}",nil可以看做是一个有效的slice
        return nil
    }

    return []int{100, 200, 300}
}

func main() {
    fmt.Println(someFunc(""))
    fmt.Println(someFunc("1"))
}
目录
相关文章
|
14天前
|
Go
Golang语言之管道channel快速入门篇
这篇文章是关于Go语言中管道(channel)的快速入门教程,涵盖了管道的基本使用、有缓冲和无缓冲管道的区别、管道的关闭、遍历、协程和管道的协同工作、单向通道的使用以及select多路复用的详细案例和解释。
47 4
Golang语言之管道channel快速入门篇
|
14天前
|
Go
Golang语言文件操作快速入门篇
这篇文章是关于Go语言文件操作快速入门的教程,涵盖了文件的读取、写入、复制操作以及使用标准库中的ioutil、bufio、os等包进行文件操作的详细案例。
37 4
Golang语言文件操作快速入门篇
|
14天前
|
Go
Golang语言之gRPC程序设计示例
这篇文章是关于Golang语言使用gRPC进行程序设计的详细教程,涵盖了RPC协议的介绍、gRPC环境的搭建、Protocol Buffers的使用、gRPC服务的编写和通信示例。
42 3
Golang语言之gRPC程序设计示例
|
14天前
|
安全 Go
Golang语言goroutine协程并发安全及锁机制
这篇文章是关于Go语言中多协程操作同一数据问题、互斥锁Mutex和读写互斥锁RWMutex的详细介绍及使用案例,涵盖了如何使用这些同步原语来解决并发访问共享资源时的数据安全问题。
37 4
|
14天前
|
Go 调度
Golang语言goroutine协程篇
这篇文章是关于Go语言goroutine协程的详细教程,涵盖了并发编程的常见术语、goroutine的创建和调度、使用sync.WaitGroup控制协程退出以及如何通过GOMAXPROCS设置程序并发时占用的CPU逻辑核心数。
23 4
Golang语言goroutine协程篇
|
14天前
|
Prometheus Cloud Native Go
Golang语言之Prometheus的日志模块使用案例
这篇文章是关于如何在Golang语言项目中使用Prometheus的日志模块的案例,包括源代码编写、编译和测试步骤。
20 3
Golang语言之Prometheus的日志模块使用案例
|
14天前
|
JSON Go 数据格式
Golang语言结构体链式编程与JSON序列化
这篇文章是关于Go语言中结构体链式编程与JSON序列化的教程,详细介绍了JSON格式的基本概念、结构体的序列化与反序列化、结构体标签的使用以及如何实现链式编程。
21 4
|
14天前
|
Go
Golang语言错误处理机制
这篇文章是关于Golang语言错误处理机制的教程,介绍了使用defer结合recover捕获错误、基于errors.New自定义错误以及使用panic抛出自定义错误的方法。
36 3
|
14天前
|
Go
Golang语言之函数(func)进阶篇
这篇文章是关于Golang语言中函数高级用法的教程,涵盖了初始化函数、匿名函数、闭包函数、高阶函数、defer关键字以及系统函数的使用和案例。
16 3
Golang语言之函数(func)进阶篇
|
14天前
|
Go
Golang语言之映射(map)快速入门篇
这篇文章是关于Go语言中映射(map)的快速入门教程,涵盖了map的定义、创建方式、基本操作如增删改查、遍历、嵌套map的使用以及相关练习题。
21 5