Golang 函數與指針|Go主題月

函數

函數就是一段基本的代碼塊,通常用來對須要重複執行的代碼進行復用。在 go 中,函數是『一等公民』,這與 js 相似,也就是能夠將函數當作一個變量進行傳遞。編程

函數聲明

因爲是強類型語言,與 js 不一樣,在函數聲明的過程當中,須要指定參數與返回值的類型。markdown

func max (n1, n2 int) int {
  var result int
  if n1 >= n2 {
    result = n1
  }
  if n1 < n2 {
    result = n2
  }
  return result
}
複製代碼

在聲明函數參數和類型的時候,與聲明變量相似,能夠一次性指定多個參數的類型,也能夠分別指定多個參數爲不一樣類型。閉包

func max (n1 int, n2 int) int {
  ……
}
複製代碼

若是函數返回值有多個,在指定返回類型的時候,須要指定每一個返回值的類型。app

func max (n1 int, n2 int) (error, int) {
  ……
  return errors.New(""), result
}
複製代碼

上面的代碼,表示返回的時候須要返回兩個值,第一個值爲 error 對象,用來表示執行期間是否出現異常。這種方式也是 Node.js 中常見的 error-first callback 的寫法。編程語言

特殊函數

在 go 中,有兩個特殊函數:maininit,這兩個函數聲明以後,通常不須要主動調用,會有自動執行的機制。函數

🔎 func main()

main 函數是 go 語言中默認的入口函數,只能應用於 package main 中,若是在其餘的 package 中不會執行。main 函數有以下幾點須要注意:oop

  • 不能定義參數;
  • 不能定義返回值;
  • 必須在 package main 中聲明;

🔎 func init()

init 函數全部的包啓動的時候都會執行,執行時機比 main 函數早,與 main 函數同樣,不能定義參數和返回值。ui

package main

import "fmt"

func init() {
	fmt.Println("執行 init 函數\n")
}
func main() {
	fmt.Println("執行 main 函數\n")
}
複製代碼

函數調用

函數的調用比較簡單,和其餘編程語言相似,只須要將函數須要接受的參數傳入其中,在執行結束後,就能獲得對應的返回值。spa

// 定義 max 函數
func max (n1, n2 int) int {
  var result int
  if n1 >= n2 {
    result = n1
  }
  if n1 < n2 {
    result = n2
  }
  return result
}

func main () {
	var result = max(5, 100)
	fmt.Println("max return", result)
}
複製代碼

匿名函數

匿名函數就是一個沒有定義函數名的函數,匿名函數能夠當成一個值,將其賦值放到某個變量中。這也是以前爲何說函數是『一等公民』,就是能夠將函數當成一個變量。3d

var max = func (n1, n2 int) int {
  var result int
  if n1 >= n2 {
    result = n1
  }
  if n1 < n2 {
    result = n2
  }
  return result
}
var result = max(5, 100)
fmt.Println("max return", result)
複製代碼

當即執行函數

因爲 go 中的函數是 『一等公民』,能夠在聲明以後當即執行,就是在函數聲明結束後,直接加上一個括號,表示該函數會當即執行,執行以後的結果能夠經過變量進行接收。

import "math"

var Pi = func () float64 {
  return math.Pi
}()

fmt.Println("PI =",Pi)
複製代碼

閉包

閉包就是可以讀取其餘函數內部變量的函數。在本質上,閉包是將函數內部和函數外部鏈接起來的橋樑。 ——百度百科

上面的描述來自百度百科,初次看概念比較難理解,若是站在使用的角度來講,閉包就是在一個函數調用後,返回另外一個匿名函數,並保持當前函數內的局部變量,能夠給匿名函數引用。

下面咱們能夠簡單實現一個迭代器函數,該函數接受一個切片,返回一個匿名函數,該匿名函數每次執行,都會取出切片的一個值,直到所有讀取。

func generate(slice []int) func() (bool, int) {
	i := 0
	length := len(slice)
	return func () (bool, int) {
		if i >= length {
			return true, 0
		}
		var result = slice[i]
		i++
		return false, result
	}
}

func main() {
	slice := []int{1, 2, 3, 4, 5}
	nextNum := generate(slice)
	done, result := nextNum()
  // 直到 done 不等於 false,才中止
	for done == false {
		fmt.Println(result, done)
		done, result = nextNum()
	}
  fmt.Println(result, done)
}
複製代碼

指針

咱們前面常說的變量指的通常是一個值,指針是指向該變量存儲在內存的位置。指針也能夠存儲在一個變量中,該變量稱爲『指針變量』。

指針變量聲明

聲明指針變量時,須要指針指向哪種類型,由於不一樣類型的值在內存佔用的空間大小不同,僅僅知道內存地址仍是不夠,還須要知道該變量在內存中佔用多大空間。聲明指針變量只須要在類型前,加上 * 便可。

var point *int // 聲明 int 類型的指針
複製代碼

指針變量賦值

給指針變量賦值,須要在對應類型的變量前加上&符號,表示取出該變量的地址。

var i = 1
var point *int
point = &i
複製代碼

值傳遞與引用傳遞

通常狀況下,咱們傳入函數的參數僅爲變量的值,這樣的傳遞被稱爲值傳遞,在函數內對參數修改也不會影響到外部變量。

func addOne(slice []int, number int) {
	slice = append(slice, number)
	fmt.Println("inner slice =",  slice)
}

slice := []int{1, 2, 3}
addOne(slice, 100)
fmt.Println("outer slice =",  slice)
複製代碼

上述代碼中,咱們寫了一個函數,會對傳入的切片追加一個值,調用以後,咱們會發現外部切片的值並無發生變量。

若是須要外部變量的值會跟隨函數調用發生變化,就須要將變量的指針傳入函數中,這樣的傳遞被稱爲引用傳遞。這樣在函數中修改參數就會影響到外部的變量了。

// 此時 slice 爲指針變量
func addOne(slice *[]int, number int) {
  // 經過 *slice 能夠取出 slice 指針對應的值
	*slice = append(*slice, number)
	fmt.Println("inner slice =",  *slice)
}

slice := []int{1, 2, 3}
addOne(&slice, 100)
fmt.Println("outer slice =",  slice)
複製代碼

相關文章
相關標籤/搜索