Golang 函數function

函數function

  • Go函數不支持嵌套、重載和默認參數
  • 但支持如下特性:

  1. 無需聲明原型
  2. 不定長度變參
  3. 多返回值
  4. 命名返回值參數
  5. 匿名函數
  6. 閉包
  • 定義函數使用關鍵字func,且左大括號不能另起一行
  • 函數也能夠做爲一種類型使用

返回值及參數說明閉包

func A(a int, b string) (int, string, int)  { //第一個小括號當中是你的參數列表,第二個小括號是你的返回值列表
    
}
func A(a, b, c int) (int, string, int)  {
    //若是abc都是int型的話,能夠按照這種方法進行簡寫,一樣的方法也適用於返回值當中

}
func A() (a, b, c int)  { //1:若是這樣寫的話就必需要命名返回值
    //命名返回值和不命名返回值得區別
}
func A() (int, int, int)  { //
    //命名返回值和不命名返回值得區別
    a, b, c := 1,2,3
    return a,b,c
    //若是此時沒有命名返回值的時候,那麼在返回值得時候就必須寫上return a,b,c
    //固然爲了代碼的可讀性,這裏咱們規定必須return 的時候加上返回值名
}

不定長變參函數

package main

import "fmt"

func main()  {
    A(1,2,3,4,5,6,7)


}

func A(a ...int) {
    // 這裏採用的是不定長變參,不定長變參必須是參數的最後一個參數,後面不能再跟 b string這樣的參數
    fmt.Println(a)
}
package main

import "fmt"

func main()  {
    s1:= []int{1,2,3,4}
    a,b :=1,2
    A(a,b)
    fmt.Println(a,b)
    B(s1)
    fmt.Println(s1)


}

func A(a ...int) {
    //這裏傳進來的其實是一個slice,引用類型
    a[0] = 3
    a[1] = 4
    //儘管咱們在函數A當中接收到的是一個slice,可是它獲得的是一個值拷貝
    //和直接傳遞一個slice的區別看函數B
    fmt.Println(a)
}
func B(s []int)  {
    //這裏並非傳遞一個指針進去,而是對這個slice的內存地址進行了一個拷貝
    //這裏還能夠看到像int型、string型進行常規的參數傳進去的話,只是進行了個值拷貝,slice傳進去雖然也是拷貝,可是它是內存地址的拷貝
    s[0] = 4
    s[1] = 5
    s[2] = 6
    s[3] = 7
    fmt.Println(s)
    //在這裏 咱們看到咱們在函數B當中的修改,實際上影響到了咱們main函數當中的變量s1
    //若是直接傳遞一個slice,它的修改就會影響到這個slice的自己

}

PS:值類型和引用類型進行函數傳參拷貝是不同的,一個是拷貝值,一個是拷貝地址
package main

import (
    "fmt"
)

func main() {
    a := 1
    A(&a) //這裏取出a的地址
    fmt.Println(a)

}

func A(a *int) { //傳遞的是指針類型
    *a = 2 //在操做的時候須要去它的值進行操做,這個時候函數A就能夠改變原始a的值
    fmt.Println(*a)
}

函數類型的使用指針

package main

import (
    "fmt"
)

func main() {
    a := A
    a() //這個時候是將A的函數類型賦值給a,在go語言當中一切皆是類型啊

}
func A() {
    fmt.Println("Func A")
}

匿名函數的使用code

package main

import (
    "fmt"
)

func main() {
    a := func() {
        //此時這個代碼塊就是一個匿名函數,這個函數自己沒有名稱,咱們將她賦值給a,而後調用
        fmt.Println("Func A")

    }
    a() //依然能夠打印func A
}

GO語言當中的閉包內存

package main

import (
    "fmt"
)

func main() {
    f := closure(10)
    res1 := f(1)
    fmt.Println(res1)
    res2 := f(2)
    fmt.Println(res2)

}

func closure(x int) func(int) int {
    fmt.Printf("%p \n", &x)
    return func(y int) int {
        fmt.Printf("%p \n", &x)
        return x + y
    }
}
//這裏能夠看出3次打印x的地址都是同樣的

defer

  • defer的執行方式相似其它語言中的析構函數,在函數執行體結束後按照調用順序的相反順序逐個執行
  • 即便函數發生嚴重錯誤也會執行
  • 支持匿名函數的調用
  • 一般用於資源清理、文件關閉、解鎖以及記錄時間等操做
  • 經過與匿名函數配合可在return以後修改函數計算結果
  • 若是函數體內某個變量做爲defer時匿名函數的參數,則在定義defer時即已經得到了拷貝,不然則是引用某個變量的地址資源

  • GO沒有異常機制,但有panic/recove模式來處理錯誤
  • Panic能夠在任何地方引起,但recover只有在defer調用的函數中有效原型

package main

import (
    "fmt"
)

func main() {
    fmt.Println("A")
    defer fmt.Println("B")
    defer fmt.Println("C")
}
//PS:打印的結果就是A C B
package main

import (
    "fmt"
)

func main() {
    for i := 0; i < 3; i++ {
        //defer fmt.Println(i)
        defer func() {
            fmt.Println(i)
        }() //調用這個函數
    }
}

//剛纔直接打印的時候,是做爲一個參數傳遞進去,運行到defer的時候是將這個i的值進行了一個拷貝,因此打印的是 2 1 0
//這種狀況下i一直是一個地址的引用,i一直引用的是局部變量的i,在退出這個循環體的時候 i已經變成了3,在main函數return的時候,開始執行defer語句,defer語句的時候i已經變成了3

異常機制string

package main

import (
    "fmt"
)

func main() {
    A()
    B()
    C()

}

func A() {
    fmt.Println("Func A")
}
func B() {
    defer func() {
        if err := recover(); err != nil {
            fmt.Println("Recover in B")

        }
    }()
    panic("Panic in B")

}
func C() {
    fmt.Println("Func C")
}
package main

import (
    "fmt"
)

func main() {
    var fs = [4]func(){}
    for i := 0; i < 4; i++ {
        defer fmt.Println("defer i=", i) //這個i是傳遞進來的參數,因此是值得拷貝
        defer func() {
            fmt.Println("defer_closure i=", i) //這裏的i是引用外部的i,因此循環結束後,i變成了4
        }()
        fs[i] = func() {
            fmt.Println("closure i = ", i) //這裏也是引用外部的i,因此循環結束後i變成了4
        }
    }
    for _, f := range fs {
        f()
    }
}
相關文章
相關標籤/搜索