Golang研學:在用好Golang指針類型

Golang指針

在大部分面嚮對象語言如C++、C#、Java,在函數傳參數時除了基礎值類型,對象是經過引用方式傳遞的。shell

然而,在Go語言中,除了map、slice和chan,全部類型(包括struct)都是值傳遞的。編程

那麼,如何在函數外使用函數內處理後的變量呢?只能經過返回新變量嗎?框架

不,可使用指針函數

大部分面嚮對象語言都不多有用到指針的場景了,可是在Go語言中有大量的指針應用場景,要成爲一名合格的Gopher,必須瞭解。學習

概念

每個變量都會分配一塊內存,數據保存在內存中,內存有一個地址,就像門牌號,經過這個地址就能夠找到裏面存儲的數據。spa

指針就是保存這個內存地址的變量。指針

在Go語言中,用&取得變量的地址code

//爲了說明類型,我採用了顯性的變量定義方法,實際開發中更多的是用「:=」自動獲取類型變量類型
var mystr string = "Hello!"
var mystrP *string = &mystr

fmt.Println(mystrP)
複製代碼

將以上代碼敲入main函數中,go run,打印出的內容就是mystr的內存地址。mystrP就是mystr的指針變量。cdn

*取得指針變量指向的內存地址的值對象

在以前的代碼的後面增長一句代碼:

fmt.Println(*mystrPointer)
複製代碼

go run 運行後,能夠看到打印出 mystr的值「Hello!」

符號*也用作定義指針類型的關鍵字。

例如:

var p *int
複製代碼

指針應用場景

在其餘OOP語言中,大多數狀況是不須要花太多時間操做指針的,如Java、C#,對象的引用操做都已經交給了虛擬機和框架。而Go常常會用到指針。緣由主要有3個:

  1. Go語言中除了map、slice、chan外,其餘類型在函數參數中都是值傳遞
  2. Go語言不是面向對象的語言,不少時候實現結構體方法時須要用指針類型實現引用結構體對象
  3. 指針也是一個類型,在實現接口interface時,結構體類型和其指針類型對接口的實現是不一樣的

接下來就分別介紹一下,期間會穿插一些簡單的代碼片斷,您能夠建立一個Go文件輸入代碼,運行體驗一下。

函數中傳遞指針參數

Go語言都是值傳遞,例如:

package main

import "fmt"

func main() {
	i := 0
	f(i)
	fmt.Println(i)
}

func f(count int) {
	fmt.Println(count)
	count++
}
複製代碼

結果:

0
0
複製代碼

i在執行先後沒有變化

若是但願被函數調用後,i的值產生變化,f函數的參數就應該改成 *int 類型。如:

func main() {
	i := 0
	f(&i)
	fmt.Println(i)
}

func f(count *int) {
	fmt.Println(*count)
	(*count)++
}
複製代碼
  1. f定義參數用 *int 替代 int,申明參數是一個int類型的指針類型
  2. 調用函數時,不能直接傳遞int的變量i,而要傳遞用&取得i的地址
  3. f函數內,參數count如今是指針了,不能直接打印,須要用*取出這個指針指向的地址裏保存的值
  4. count的取值+1.
  5. 調用f函數,在主函數main裏打印i

能夠看到結果

0
1
複製代碼

i的值改變了。

Struct結構體指針類型方法

Go語言中給結構體定義方法

//定義一個結構體類型
type myStruct struct {
	Name string
}

//定義這個結構體的更名方法
func (m myStruct) ChangeName(newName string) {
	m.Name = newName
}

func main() {
	//建立這個結構體變量
	mystruct := myStruct{
		Name: "zeta",
	}

	//調用更名函數
	mystruct.ChangeName("Chow")

	//沒改爲功
	fmt.Println(mystruct.Name)
}
複製代碼

這樣的方法不會改掉結構體變量內的字段值。 就算是結構體方法,若是不使用指針,方法內仍是傳遞結構體的值。

如今咱們改用指針類型定義結構體方法看看。

只修改 ChangeName 函數,用*myStruct類型替代myStruct

func (m *myStruct) ChangeName(newName string) {
	m.Name = newName
}
複製代碼

再運行一次,能夠看到打印出來的名字改變了。

當使用指針類型定義方法後,結構體類型的變量調用方法時會自動取得該結構體的指針類型並傳入方法。

指針類型的接口實現

最近在某問答平臺回答了一個Gopher的問題,大體內容是問爲何不能用結構體指針類型實現接口方法?

看一下代碼

//定義一個接口
type myInterface interface {
	ChangeName(string)
	SayMyName()
}

//定義一個結構體類型
type myStruct struct {
	Name string
}

//定義這個結構體的更名方法
func (m *myStruct) ChangeName(newName string) {
	m.Name = newName
}

func (m myStruct) SayMyName() {
	fmt.Println(m.Name)
}

//一個使用接口做爲參數的函數
func SetName(s myInterface, name string) {
	s.ChangeName(name)
}

func main() {
	//建立這個結構體變量
	mystruct := myStruct{
		Name: "zeta",
	}

	SetName(mystruct, "Chow")

	mystruct.SayMyName()
}
複製代碼

這段代碼是沒法編譯經過的,會提示

cannot use mystruct (type myStruct) as type myInterface in argument to SetName:
        myStruct does not implement myInterface (ChangeName method has pointer receiver)
複製代碼

myStruct類型沒有實現接口方法ChangeName,也就是說func (m *myStruct) ChangeName(newName string) 並不算實現了接口,由於它是*myStruct類型實現的,而不是myStruct

改一改

在調用SetName時,用&mystruct 替代 mystruct:

SetName(&mystruct, "Chow")
複製代碼

編譯運行,成功。

爲何結構體類型實現的接口該結構體的指針類型也算實現了,而指針類型實現的接口,不算是該結構體實現了接口呢?

** 緣由是,結構體類型定義的方法能夠被該結構體的指針類型調用;而結構體類型調用該指針類型的方法時是被轉換成指針,不是直接調用。**

因此,&mystruct 直接實現了接口定義的ChangeNameSayMyName兩個方法,而mystruct只能實現了SayMyNamemystruct調用ChangeName方法其實轉換成指針類型後調用的,不算實現了接口。


到此Go語言指針類型的應用介紹差很少了。

總結一下:

  1. Go語言中指針很是經常使用,必定要掌握
  2. Go語言除了map、slice、chan其餘都是值傳遞,引用傳遞必定要用指針類型
  3. 結構體類型定義方法要注意使用指針類型
  4. 接口實現方法時,用指針類型實現的接口函數只能算是指針類型實現的,用結構體類型實現的方法也做爲是指針類型實現。

歡迎你們一塊兒討論、學習Go語言!!

歡迎關注公衆號,和你們一塊兒學習編程吧

曉代碼公衆號
相關文章
相關標籤/搜索