函数

 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,那么作用域仅在该代码块中有效

原文地址:https://www.cnblogs.com/yizhixiaowenzi/p/14572776.html