Go語言入門系列前面的文章:算法
在Go語言入門系列(二)之基礎語法總結這篇文章中已經介紹過了Go語言的函數的基本使用,包括聲明、參數、返回值。本文再詳細介紹一下函數的其餘使用。編程
Go語言的函數除了支持0個或多個參數,還支持不定數量的參數,即變參。聲明方式爲:數組
func foo(變參名 ...參數類型) 函數類型 { //函數體 }
下面是一個具體的函數,它接收不定數量的int
參數,並返回和:數據結構
package main import "fmt" func add(arg ...int) int { //變參函數 var sum int for _, value := range arg { sum += value } return sum } func main() { sum := add(1, 2, 3, 4) fmt.Println(sum) //10 }
arg ...int
代表add
函數接收不定數量的參數,且只能是int
類型的。arg
是咱們給該變參取的名字,它其實是一個切片,因此在add
函數中可使用range
遍歷變量arg
。app
當咱們調用一個有參函數時,確定會向該函數中傳入參數:數據結構和算法
package main import "fmt" //傳入一個值,打印它 func printX(x int) { fmt.Println(x) } func main() { var a int = 5 printX(a) //向函數中傳入參數:變量a }
這裏有一個問題:咱們真的是把變量a
傳給了printX
函數嗎?咱們用兩個例子來講明問題。函數
package main import "fmt" func plusOne(x int) int { x = x + 1 fmt.Println("執行加一") return x } func main() { a := 5 fmt.Println("a =", a) //應該爲5 實際爲5 b := plusOne(a) fmt.Println("a =", a) //應該爲6 實際爲5 fmt.Println("b =", b) //應該爲6 實際爲6 }
plusOne
函數的做用是把傳進來的參數加一,並返回結果。指針
a=5
傳進plusOne
函數,執行了x = x + 1
語句,那麼執行事後a
的值應該爲6,但實際爲5。code
變量b
接收了函數的返回值,因此爲6,這沒問題。get
這證實了,咱們的a
變量根本就沒傳進函數中,那麼實際傳的是什麼?實際傳的是a
變量的一份拷貝。
因此,咱們向Go語言中的函數傳入一個值,實際上傳的是該值的拷貝,而非該值自己。
那若是咱們確實要把上例中的變量a
傳入plusOne
函數中呢?那此時就不該該傳值了,而是應該傳入指針。代碼改進以下:
package main import "fmt" func plusOne(x *int) int { //參數是指針變量 *x = *x + 1 fmt.Println("執行加一") return *x } func main() { a := 5 fmt.Println("a =", a) //應該爲5 實際爲5 b := plusOne(&a) //傳入地址 fmt.Println("a =", a) //應該爲6 實際爲6 fmt.Println("b =", b) //應該爲6 實際爲6 }
a=5
傳進plusOne
函數,執行了x = x + 1
語句,執行事後a
的值實際爲6。
這就證實,變量a
確實被傳進plusOne
函數並被修改了。由於咱們傳進去的是一個指針,即變量的地址,有了地址咱們能夠直接操做變量。
若是你對指針的使用不熟悉,這裏的代碼可能會有點難理解,下面逐行解釋:
func plusOne(x *int) int {
聲明x
是一個int
類型的指針參數,只接受int
類型變量的地址 。
*x = *x + 1
使用*
操做符根據x
中存的地址,獲取到對應的值,而後加一。
return *x
使用*
操做符根據x
中存的地址,獲取到對應的值,而後返回。
b := plusOne(&a)
plusOne
函數只接受int
類型變量的地址,因此使用&
操做符獲取a
變量的地址,而後才傳入。
下面我再舉一個經典的例子:寫一個函數,可以交換兩個變量的值。
若是你不知道什麼是傳值和傳指針,那可能會寫成這樣:
package main import "fmt" func swap(x, y int) { tmp := x x = y y = tmp fmt.Println("函數中:x =", x, ", y =", y) } func main() { x, y := 2, 8 fmt.Println("交換前:x =", x, ", y =", y) swap(x, y) fmt.Println("交換後:x =", x, ", y =", y) }
運行結果:
交換前:x = 2 , y = 8 函數中:x = 8 , y = 2 交換後:x = 2 , y = 8
只在函數中完成了交換,出了函數又變回原樣了。
想要完成交換,就必須傳入指針,而非值拷貝:
package main import "fmt" func swap(x, y *int) { tmp := *x *x = *y *y = tmp fmt.Println("函數中:x =", *x, ", y =", *y) } func main() { x, y := 2, 8 fmt.Println("交換前:x =", x, ", y =", y) swap(&x, &y) fmt.Println("交換後:x =", x, ", y =", y) }
運行結果:
交換前:x = 2 , y = 8 函數中:x = 8 , y = 2 交換後:x = 8 , y = 2
傳入指針可以真正交換兩個變量的值。
傳入指針的好處:
在Go語言中,函數也能夠做爲值來傳遞。下面是一個例子:
package main import "fmt" type calculate func(int, int) int // 聲明瞭一個函數類型 func sum(x, y int) int { return x + y } func product(x, y int) int { return x * y } func choose(a, b int, f calculate) int { //函數做爲參數 return f(a, b) } func main(){ diff := func(x, y int) int { //函數做爲值賦給diff return x - y } fmt.Println(choose(2, 3, sum)) //5 fmt.Println(choose(4, 5, product)) //20 fmt.Println(choose(6, 7, diff)) //-1 fmt.Println(diff(9, 8)) //1 }
函數做爲值或者參數確定要有對應的類型,類型是:func(參數類型)返回值類型
。好比func(int,int) int
可使用type
關鍵字給func(int,int) int
起個別名叫calculate
,方便使用。
choose
函數中聲明瞭一個類型爲calculate
的函數參數f
,而咱們又編寫了calculate
類型的函數sum
和product
,因此能夠向choose
函數中傳入這兩個函數。
咱們給變量diff
賦了一個函數,因此可以使用diff(9, 8)
,或者將其做爲參數傳入choose
函數。
我是「行小觀」,於千萬人中的一個普通人。陰差陽錯地走上了編程這條路,既然走上了這條路,那麼我會盡量遠地走下去。
我會在公衆號『行人觀學』中持續更新「Java」、「Go」、「數據結構和算法」、「計算機基礎」等相關文章。
歡迎關注,咱們一塊兒踏上行程。
本文章屬於系列文章「Go語言入門系列」。
若有錯誤,還請指正。