• 函数


     go中,函数分为 自定义函数 和 系统函数

    func  函数名(形参列表)  (返回值类型列表)  {

      执行语句...

      return 返回值列表

    }

    package main
    import "fmt"
    
    func cal(n1 int,n2 int,str string) int {
        var res int
        switch str {
            case "+":
                res = n1 + n2
            case "-":
                res = n1 - n2
            case "*":
                res = n1 * n2
            case "/":
                    res = n1 / n2
            default:
                fmt.Println("操作符错误")
        }
        return res
    }    
    
    func main() {
        n1 := 10
        n2 := 2
        var string = "/"
        result := cal(n1,n2,string)
        fmt.Println("result=",result)
    
    }
    代码示例

    函数的调用过程:

    调用函数时,会给该函数分配一个新的空间,编译器会通过自身的处理让这个新的空间和其他栈的空间区分开来

    在每个函数对应的栈中,数据空间是独立的,不会混淆

    当一个函数执行完毕后,程序会销毁这个函数对应的栈空间

    return

    go函数支持返回多个值

    如果返回多个值,在接收时,希望忽略某个返回值,则使用_符号表示占位忽略

    如果返回值只有一个,(返回值类型列表)  可以不写 ()

    递归函数:一个函数在函数体内又调用了本身

    package main
    import "fmt"
    
    func test(n int) {
        if n > 2 {
            n --
            test(n)
        }
        fmt.Printf("n=",n)
    }
    
    func main() {
        test(4)
    }
    代码示例

    执行一个函数时,就会创建一个新的受保护的独立空间(新函数栈)

    函数的局部变量是独立的,不会相互影响

    递归必须有明确的结束条件,并向结束条件逼近,否则会无限循环

    当一个函数执行完毕时/或者遇到return,就会返回,遵循谁调用返回给谁,同时函数本身也会被销毁

    *** 函数使用的注意事项与细节

    函数的形参列表可以是多个,返回值列表也可以是多个

    形参列表和返回值列表的数据类型,可以是值类型和引用类型

    函数的命名遵循标识符命名规范,首字母不能是数字,首字母大写可以被本包和其它包文件使用,首字母小写只能被本包文件使用

    函数内的变量是局部的,函数外不生效

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

        
    func test(n int) {
        n = n + 10
        fmt.Printf("test_n=",n)
    }
    
    func main() {
        n := 20
        test(n)
        fmt.Printf("main_n=",n)
    }
    代码示例

     如果希望在函数内修改函数外的变量,可以传入变量的地址&,函数内以指针的方式操作变量。

        
    package main
    
    import "fmt"
    
    func test(n *int) {
        *n = *n + 10
        fmt.Println("test_n=",*n)
    }
    
    func main() {
        n1 := 20
        test(&n1)
        fmt.Println("main_n1=",n1)
    }
    代码示例

     go函数不支持函数重载(即函数重新定义)

    在go中,函数也是一种数据类型,可以赋值给一个变量,则该变量就是一个函数类型的变量,通过该变量,可以对函数进行调用

        
    package main
    
    import "fmt"
    
    func test() {
        fmt.Println(111111)
    }
    
    func main() {
        a := test
        a()
        fmt.Printf("a的数据类型是%T,test的数据类型是%T",a,test)        // 数据类型都是 func()
    }
    代码示例

     函数是一种数据类型,在go中,函数可以作为形参,并且调用

        
    package main
    
    import "fmt"
    
    func getSum(n1 int,n2 int) int {
        return n1 + n2
    }
    
    func myFun(funvar func(int,int) int,num1 int,num2 int) int {
        return funvar(num1,num2)
    }
    
    func main() {
        res2 := myFun(getSum,50,60)
        fmt.Println("res2=",res2)
    }
    代码示例

     go支持自定义数据类型:

        type  自定义数据类型名字  数据类型 (相当于取别名)

        type  myInt  int   (myInt 等价于  int)

        
    package main
    
    import "fmt"
    
    func main() {
        type myInt int
    
        var num1 myInt
        var num2 int
    
        num1 = 40
        num2 = int(num1)        // go认为myInt与int是两个类型,所以需要转换
    
        fmt.Println("num1=",num1)
        fmt.Println("num2=",num2)
    }
    代码示例

    支持对函数返回值命名

        
    package main
    
    import "fmt"
    
    func test() (a int,b int) {
        return 1,2
    }
    
    func main() {
        n1,n2 := test()
        fmt.Println(n1)
        fmt.Println(n2)
    }
    代码示例

    使用_标识符,忽略返回值

        
    package main
    
    import "fmt"
    
    func test() (a int,b int) {
        return 1,2
    }
    
    func main() {
        n1,_ := test()
        fmt.Println(n1)
    }
    代码示例

    支持可变参数  args

        
    package main
    
    import "fmt"
    
    // func sum(args... int) int {                    //    0到多个参数
    func sum(n1 int,args... int) int {                //    1到多个参数,args是slice切片,通过 args[index] 访问每一个值
        sum := n1
        for i := 0 ; i < len(args) ; i++ {
            sum += args[i]
        }
        return sum
    }
    
    func main() {
        res := sum(1,2,3,4,5,6)
        fmt.Println(res)
    }
    代码示例

    init函数

    每一个源文件都可以包含一个init函数,该函数会在main函数执行前,被go运行框架调用,也就是说,init会在main函数前被调用

    如果文件同时包含 全局变量,main,init,执行流程是     全局变量 > init > main

    init函数作用:完成一些初始化的工作

    package main
    
    import "fmt"
    
    var n = test()
    
    func test() string {
        fmt.Println("--test--")
        return "无所谓"
    }
    
    func init() {
        fmt.Println("--init--")
    }
    
    func main() {
        fmt.Println("--main--")
        fmt.Println(n)
    }
    代码示例

    匿名函数

    没有名字的函数,如果函数只使用一次,可以使用匿名函数

        
    package main
    
    import "fmt"
    
    func main() {
        n := func (n1 int,n2 int) int {
            return n1 + n2
        }(10,20)
        fmt.Println(n)
    }
    代码示例

    也可以实现多次调用  (吧函数赋值给变量)

        
    package main
    
    import "fmt"
    
    func main() {
        a := func (n1 int,n2 int) int {
            return n1 + n2
        }
        
        p := a(10,20)
        q := a(20,30)
        fmt.Println(p,q)
    }
    代码示例

    全局匿名函数

        如果将匿名函数赋值给一个全局变量,那么这个匿名函数,就成为全局匿名函数,可以在程序中有效 

        
    package main
    
    import "fmt"
    
    var (
        Fun1 = func (n1 int,n2 int) int {
            return n1 + n2
        }
    )
    
    func main() {
        n := Fun1(10,20)
        fmt.Println(n)
    }
    代码示例

    闭包

    内部函数引用了外部函数的变量,被内部函数引用的变量,不会因为外部函数结束而被释放掉,而是一直存在内存中,直到内部函数被调用结束。

        
    package main
    
    import (
        "fmt"
    )
    
    func a() func() int {        // 函数a 的返回值是func,func的返回值是int
        i := 0
        fmt.Println("a",i)
        b := func() int {
            i++
            fmt.Println("func",i)
            return i
        }
        return b
    }
    
    func main() {
        a()            // a 0
        c := a()    // a 0
        c()            // func 1
        c()            // func 2
        c()            // func 3
        a()            // a 0
        
    }
    View Code

    函数的defer

    在函数中,程序员需要创建资源(比如数据库连接、文件句柄、锁等),为了在函数执行完毕后,及时的释放资源,引入了 defer(延时机制)

        
    package main
    
    import "fmt"
    
    func sum(n1 int,n2 int) int {
        // 当执行到defer时,本行暂不执行,会将defer后面的语句压入到独立的栈中(defer栈)
        // 当函数执行完毕后,再从defer栈,按照后入先出的方式出栈,执行
        defer fmt.Println("3",n1)
        defer fmt.Println("2",n2)
    
        res := n1 + n2
        defer fmt.Println("1",res)
        return res
    }
    
    func main() {
        n := sum(10,20)
        fmt.Println("4",n)
    }
    代码示例

     注意事项与细节

      当执行到defer时,本行暂不执行,会将defer后面的语句压入到独立的栈中(defer栈),然后继续执行函数的下一个语句
      当函数执行完毕后,再从defer栈,按照后入先出的方式出栈,执行
      在defer将语句放入到栈时,也会将相关的值拷贝同时入栈,
      
    package main
    
    import "fmt"
    
    func sum(n1 int,n2 int) int {
        // 当执行到defer时,本行暂不执行,会将defer后面的语句压入到独立的栈中(defer栈)
        // 当函数执行完毕后,再从defer栈,按照后入先出的方式出栈,执行
        defer fmt.Println("3,n1",n1)
        defer fmt.Println("2,n2",n2)
    
        n1 ++
        n2 ++    
        
        res := n1 + n2
        defer fmt.Println("1",res)            // 32
        return res
    }
    
    func main() {
        n := sum(10,20)
        fmt.Println("4",n)
    }
    代码示例

    函数参数传递方式

    值传递与引用传递,值类型参数默认是值传递,引用类型参数默认是引用传递

    不管是值传递还是引用传递,传递给函数的都是变量的副本,不同的是,值传递的是值的拷贝,引用传递的是地址的拷贝。一般来说,地址拷贝效率高,因为数据量小,而值拷贝效率决定于数据量,数据量越大,效率越低。

    值类型:int,float,bool,string,数组,结构体

    引用类型:指针,slice切片,map,管道chan,interface 等

    值传递与引用传递的特点:

    •   值传递:值类型默认是值传递,变量直接存储值,内存通常在栈中分配。
    •   引用传递:引用类型默认是引用传递,变量存储的是一个地址,这个地址对应的空间才真正存储数据,内存通常在堆上分配,当没有任何变量引用这个地址时,该地址对应的数据空间就成为一个垃圾,由GC来回收。
    •   如果希望函数内的变量能修改函数外的变量,可以传入变量的地址&,函数内以指针的方式操作变量。

    变量的作用域

    函数内声明的变量叫局部变量,作用域仅限于函数内部

    函数外部声明的变量叫全局变量,作用域在整个包都有效,如果首字母大写,则作用域在整个程序有效

    如果作用域在一个代码块中,比如if/for,那么作用域仅在该代码块中有效

  • 相关阅读:
    PHP获取汉字拼音首字母
    记录,待总结5
    HDU2833 WuKong Floyd
    搜索
    记录,待总结4
    HDU3350 #define is unsafe 栈的应用
    指针与引用的混合使用总结
    多源最短路径 Floyd
    引用总结
    函数返回值总结
  • 原文地址:https://www.cnblogs.com/yizhixiaowenzi/p/14572776.html
Copyright © 2020-2023  润新知