06-GoLang函数

函数格式

  • C语言中
在C语言中函数只能返回一个值

返回值类型 函数名称(形参列表){
  逻辑语句;
    }
  • Go语言中
在Go语言中函数可以返回多个值

func 函数名称(形参列表) (返回值列表){
  逻辑语句;
    }
package main
import "fmt"
func main() {
    //1.返回一个值
    var num int = getValue()
    fmt.Printf("%d\n", num)
    //2.返回两个值
    num1, num2 := calculate()
    fmt.Printf("%d, %d\n", num1, num2)
}

func getValue()(int) {
    return  666
}
func calculate()(int, int){
    return 10, 20
}


C语言函数的类型

  • 1.没有返回值没有形参的函数
  • .2.有返回值没有形参的函数
  • 3.没有返回值有形参的函数
  • 4.有返回值有形参的函数

Go语言函数的类型和C语言一样

注意点:

  • 在C语言中如果函数的定义写在函数的调用之前必须先声明再调用
  • 在Go语言中如果函数的定义写在函数的调用之前, 不用声明就可以调用, Go语言没有声明(处于同级作用域的情况)
  • 定义在全局作用域中任何地方的函数,main函数中可以任意调用
  • Go语言函数可以没有返回值或形参
//没有返回值没有形参
func say(){
    fmt.Println("没有返回值没有形参")
}
//没有返回值有形参的函数
func sum(a , b int){
    res := a + b
    fmt.Println(res)
}
//有返回值有形参的函数
func minus(a, b int)(res int){
    res = a - b
    return
}
  • Go语言中如果函数只有一个返回值, 那么返回值列表的()可以省略
func minus(a int, b int)(int){
    res := a - b
    return res
}
//省略
func getNumber()int{
    return 888
}
  • Go语言中如果有返回值, 可以在返回值列表中定义变量, 然后在函数体中直接使用
func getValue() (num ,value int){
    return 666, 888
//注意:外界要定义两个变量接收
}
  • 如果在返回值列表中定义了变量, 那么return后面可以不用写任何内容, 默认就会将返回值列表中定义的变量返回
func getNumber() (num int){
    num = 999
    return
}
  • Go语言中一个函数可以有多个返回值
func getValue() (int, int){
    return 666, 888
}
  • 如果一个函数有多个返回值, 但是我们只想使用其中的一个, 那么可以在接收的时候使用_来忽略, 不需要的那个返回值
package main
import "fmt"
func main()  {
a, _ := getValue()
    fmt.Println("a = ", a)
}
func getValue() (num ,value int){
    return 666, 888
}

  • 因为下划线在Go语言中有特殊的含义, 所以在定义标识符的时候下划线不能单独作为标识符
  • 如果返回值列表中有多个返回值, 并且连续的多个返回值的数据类型都一样, 那么只用在最后一个变量后面添加数据类型即可
  • 如果形参列表中有多个形参, 并且连续的多个形参的数据类型都一样, 那么只用在最后一个变量后面添加数据类型即可

函数参数

  • 在C语言中如果基本数据类型作为函数的参数, 是值传递, 在函数内修改形参不会影响到外界的实参
package main
import "fmt"
func main() {
    num := 666
    change1(num)
    fmt.Println(num)   //666
}
func change1(a int) {
    a = 789
}
  • Go语言和C语言一样/ int float bool
  • 如果想在函数内部修改外界传入的实参, 那么必须传入指针
package main

import "fmt"

func main() {
    num := 666
    change2(&num)
    fmt.Println(num)   //789
}
func change2(a *int) {
    *a = 789
}

匿名函数

  • 什么是匿名函数?
    普通的函数都是有名字的函数, 而匿名函数就是没有名字的函数

  • 匿名函数的应用场景:
    一般情况下某个函数只会被调用一次, 我们就会使用匿名函数

 // 直接定义一个函数, 然后立即调用这个函数
     func(){
        fmt.Println("匿名函数")
     }()

1、作为其他函数的参数

package main
import "fmt"
func main() {
//匿名函数作其他函数参数
result := calculate(10, 20, func(a, b int) int {
        return  a + b
    })
}
//定义函数时将匿名函数类型作为参数传递,此时的函数有临时的名字,即method
func calculate(a, b int, method func(int, int) int) (int) {
    res := method(a, b)
    return res
}
package main
import "fmt"
func main() {
     //此处限定一个
    test(func() {
        fmt.Println("匿名函数")
    })
}
func test(method func())  {
    fmt.Printf("%p\n", method)  //0x48d160
    method()
}

2、作为其他函数的返回值

package main
import "fmt"
func main() {
    res := test()
    res(10, 20)
}
func test() func(int, int) {
    return func(a int, b int) {
        fmt.Println(a + b)
    }
}

注意点:

  • 匿名函数定义之后, 如果没有立即调用, 编译器会报错
  • 可以将一个匿名函数保存到一个变量中
  • 在C语言中我们知道, 我们可以定义一个指向函数的指针来保存函数的地址, 然后通过指针来调用函数
    int test(int a, int b){}
    int (*test)(int, int)
  • 在Go语言中函数是一等公民(可以定义函数类型变量, 作为形参, 作为返回值), 所以在Go语言中也可以用函数类型来定义变量(相当于C语言中指向函数的指针)
  • 由于函数是一等公民, 所以以前定义变量所有的格式, 都可以用于定义函数类型的变量
    格式
  • 格式一
    var 变量名称 函数类型
    // 代表定义了一个名称叫做fn的变量, 这个变量的类型是一个函数  func(int, int) int
     // 这个函数可以接收两个int类型的形参, 并且可以返回一个int类型的结果
     // 也就是说变量fn将来可以保存所有接收两个int类型形参返回一个int类型结果的函数
     var fn func(int, int) int
     fn = func(a int, b int) int {
         return a + b
     }
  • 格式二
    var 变量名称 函数类型 = 匿名函数
var fn func(int, int) int = func(a int, b int) int {
        return a + b
     }
// 相当于
 func fn(a int, b int) int {
        return a + b
     }
 fmt.Printf("%T\n", fn) // func(int, int) int

  • 格式三
    var 变量名称 = 匿名函数
var f= func() {
        fmt.Println("匿名函数")
    }
  • 格式四
    变量名称 := 匿名函数
     // 定义一个函数类型的变量, 保存匿名函数, 然后通过函数类型的变量调用匿名函数
     f := func() {
        fmt.Println("匿名函数")
    }
     fmt.Printf("%T\n", f)   //func()
  • 格式五
    var(
    var 变量名称 = 匿名函数
    )
  • 同类型的函数类型变量才可以相互赋值
    var fn func(int,int)int
    fn = func(a int, b int) int{
        return a + b
    }
    var method func(int, int)int
    method = fn
  • 匿名函数demo
package main
import (
    "fmt"
)
func main() {

    //work(func() {
    //  fmt.Println("参与部门会议")
    //  fmt.Println("修改BUG")
    //  fmt.Println("完成老大安排的任务")
    //  fmt.Println("... ...")
    //  fmt.Println("提交代码")
    //})

    work(func() {
        fmt.Println("组织部门会议")
        fmt.Println("给部门的员工分配任务")
        fmt.Println("检查部门员工昨天提交的代码")
        fmt.Println("... ...")
    })
}
// 攻城狮的一天
func work(method func()){
    // 1.上班前
    func(){
        fmt.Println("起床")
        fmt.Println("刷牙洗脸")
        fmt.Println("吃早餐")
        fmt.Println("做地铁")
        fmt.Println("打卡")
        fmt.Println("打开电脑")
    }()

    // 2.上班中
    method()

    // 3.下班之后
    func(){
        fmt.Println("关电脑")
        fmt.Println("打卡")
        fmt.Println("坐地铁")
        fmt.Println("到家")
        fmt.Println("吃饭")
        fmt.Println("娱乐")
        fmt.Println("刷牙洗脸")
        fmt.Println("睡觉")
    }()

}

// 项目经理的一天
func work2(){
    fmt.Println("起床")
    fmt.Println("刷牙洗脸")
    fmt.Println("吃早餐")
    fmt.Println("做地铁")
    fmt.Println("打卡")
    fmt.Println("打开电脑")

    fmt.Println("组织部门会议")
    fmt.Println("给部门的员工分配任务")
    fmt.Println("检查部门员工昨天提交的代码")
    fmt.Println("... ...")

    fmt.Println("关电脑")
    fmt.Println("打卡")
    fmt.Println("坐地铁")
    fmt.Println("到家")
    fmt.Println("吃饭")
    fmt.Println("娱乐")
    fmt.Println("刷牙洗脸")
    fmt.Println("睡觉")
}
// 程序员的一天
func work1()  {
    fmt.Println("起床")
    fmt.Println("刷牙洗脸")
    fmt.Println("吃早餐")
    fmt.Println("做地铁")
    fmt.Println("打卡")
    fmt.Println("打开电脑")

    fmt.Println("参与部门会议")
    fmt.Println("修改BUG")
    fmt.Println("完成老大安排的任务")
    fmt.Println("... ...")
    fmt.Println("提交代码")

    fmt.Println("关电脑")
    fmt.Println("打卡")
    fmt.Println("坐地铁")
    fmt.Println("到家")
    fmt.Println("吃饭")
    fmt.Println("娱乐")
    fmt.Println("刷牙洗脸")
    fmt.Println("睡觉")
}


闭包

  • 什么是闭包?
    闭包是一个特殊的匿名函数, 它是匿名函数和相关引用环境组成的一个整体,只要匿名函数中用到了外界的变量, 那么我们就把匿名函数叫做闭包
  • 只要闭包还在使用外界的变量, 那么外界的变量就会一直存在(和js作用域链有异曲同工之妙)
package main
import "fmt"
func main() {
     num := 10
     a := func() {
        num++
        fmt.Println(num)
     }
     a() // 11
     a() // 12
     a() // 13
     // addUpper会返回一个闭包, 那么fn中保存的就是这个闭包, 那么fn就等于闭包
     // 所以按照Go语言的规则, 只要fn还在, addUpper中被闭包引用的变量就不会释放
     fn := addUpper()
     fn()
     fn()
     fn()
}
func addUpper() func() {
    x := 1
    return func() {
        x++
        fmt.Println(x) // 2 , 3 , 4
    }
}

延迟调用

  • Go语言中没有提供其它面向对象语言的析构函数, 但是Go语言提供了defer语句用于实现其它面向对象语言析构函数的功能
  • defer语句常用于释放资源解除锁定以及错误处理
    • 例如C语言中我们申请了一块内存空间,那么不使用时我们就必须释放这块存储空间
    • 例如C语言中我们打开了一个文件,那么我们不使用时就要关闭这个文件
    • 例如C语言中我们打开了一个数据库, 那么我们不使用时就要关闭这个数据库
    • 这一类的操作在Go语言中都可以通过defer语句来完成
  • 无论你在什么地方注册defer语句,它都会在所属函数执行完毕之后才会执行, 并且如果注册了多个defer语句,那么它们会按照后进先出的原则执行
    • 正是因为defer语句的这种特性, 所以在Go语言中关闭资源不用像C语言那样用完了再关闭, 我们完全可以打开的同时就关闭, 因为无论如何defer语句都会在所属函数执行完毕之后才会执行
  package main
  import "fmt"
  func main() {
    defer fmt.Println("我是第一个被注册的") // 3
    fmt.Println("main函数中调用的Println") // 1
    defer fmt.Println("我是第二个被注册的") // 2
  }

package main
import "fmt"
func main() {
    /*
    1.defer语句的格式
    defer 语句

    2.defer语句的作用
    延迟执行, 会在所属函数执行完毕之后再执行
    常用于资源释放, 解锁, 错误处理等等
    以前在C语言中我们说过, 通过malloc申请的存储空间, 一定更要释放free
    但是由于我们不能写完malloc之后立即free,所以经常导致我们忘记释放
    malloc -- >申请存储空间
    ... ... --> 使用存储空间
    free --> 释放存储空间
    
    注意点:
    如果在同一个函数中编写了多个defer语句, 那么会遵守先进后出的原则
    先注册的defer语句后执行, 后注册的defer语句先执行
     */

     //fmt.Println("申请存储空间")
     //defer fmt.Println("释放存储空间")
     //fmt.Println("使用存储空间")
     //fmt.Println("使用存储空间")
     //fmt.Println("使用存储空间")

     defer fmt.Println("第一条语句")
     defer fmt.Println("第二条语句")
     defer fmt.Println("第三条语句")
     defer fmt.Println("第四条语句")
}


init函数

  • golang里面有两个保留的函数:
    • init函数(能够应用于所有的package)
    • main函数(只能应用于package main)
    • 这两个函数在定义时不能有任何的参数和返回值
  • go程序会自动调用init()和main(),所以你不能在任何地方调用这两个函数
  • package main必须包含一个main函数, 但是每个package中的init函数都是可选的
  • 一个package里面可以写任意多个init函数,但这无论是对于可读性还是以后的可维护性来说,我们都强烈建议用户在一个package中每个文件只写一个init函数
  • 单个包中代码执行顺序如下
    • main包-->常量-->全局变量-->init函数-->main函数-->Exit
package main
import  "fmt"
const constValue  = 998 // 1
var gloalVarValue int = abc() // 2
func init() { // 3
    fmt.Println("执行main包中main.go中init函数")
}
func main() { // 4
    fmt.Println("执行main包中main.go中main函数")
}
func abc() int {
    fmt.Println("执行main包中全局变量初始化")
    return 998
}

  • 多个包之间代码执行顺序如下


  • init函数的作用

    • init函数用于处理当前文件的初始化操作, 在使用某个文件时的一些准备工作应该放到这里

输入输出函数

输出函数

  • 在C语言中, 我们使用printf来输出内容

  • 在Go语言中也可以使用printf输出内容
    第一种方式:
    格式:fmt.Printf("格式化字符串", 数据列表)
    特点: 不会自动换行, 但是可以自定义输出格式

    num, value := 10, 20
    fmt.Printf("num = %d, value = %d\n", num, value)
    fmt.Printf("------")
  • 但是在Go语言中还有其它更方便的函数, 也可以输出内容
    第二种方式:
    格式:fmt.Println(数据列表)
    特点: 会自动换行, 但是不能使用占位符%d%c%s
    num, value := 10, 20
    fmt.Println("num = ", num, "value = ",value)
    fmt.Println("----")

输入函数

  • 在C语言中, 我们使用scanf来接收输入的内容

  • Go语言中也可以使用scanf来接收输入的内容
    第一种方式:
    格式: fmt.Scan(地址列表)
    特点: 如果接收的不是字符串类型(%c), 会忽略空格和TAB和回车, 相当于C语言的scanf

    var num int;
    var value int;
    fmt.Scan(&num, &value)
    fmt.Println(num, value)
  • 在Go语言中还有其它更方便的函数, 也可以接收输入的内容
    第二种方式:
    格式:fmt.Scanf(格式化字符串, 地址列表)
    特点: 如果接收的不是字符串类型(%c), 会忽略空格和TAB, 但是不会忽略回车
    var num int;
    var value int;
    fmt.Scanf("%d", &num)
    fmt.Println(num, value)

第三种方式:
格式:fmt.Scanln(地址列表)
特点: 如果接收的不是字符串类型(%c), 会忽略空格和TAB, 但是不会忽略回车

    var num int;
    var value int;
    fmt.Scanln(&num, &value)
    fmt.Println(num, value)

你可能感兴趣的:(06-GoLang函数)