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)
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 216,142评论 6 498
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,298评论 3 392
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 162,068评论 0 351
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,081评论 1 291
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,099评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,071评论 1 295
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,990评论 3 417
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,832评论 0 273
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,274评论 1 310
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,488评论 2 331
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,649评论 1 347
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,378评论 5 343
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,979评论 3 325
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,625评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,796评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,643评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,545评论 2 352

推荐阅读更多精彩内容

  • 在C语言中,五种基本数据类型存储空间长度的排列顺序是: A)char B)char=int<=float C)ch...
    夏天再来阅读 3,340评论 0 2
  • 夏夜的风 清凉如水 从树的枝头 无声滑过 摇碎了月影 也湮没了虫鸣 如果 把我的思念 叠做一叶小舟 这如水的风 可...
    潇潇雨疏阅读 1,727评论 30 78
  • 随着长大,慢慢地变忙了,或者说装似很忙了,忙得没时间陪爸妈好好聊天,忙得没时间按时睡觉,忙得没时间联系老友……...
    等水的咸鱼阅读 196评论 0 2
  • 五年前还是可望而不可及的事,今天成了现实。终于拥有了一台自己的电脑,而且还是笔记本的,我很兴奋。这是一台双核、1个...
    开宗明义阅读 347评论 11 0
  • 今年,伴随着女儿高中毕业,我们也结束了三年在学校周边租房陪读的生活。重新回到自己熟悉的家生活,一切都开始重新进入往...
    走在雨的缝中阅读 817评论 2 3