golang中值類(lèi)型/指針類(lèi)型的變量區(qū)別總結(jié)
前言
值類(lèi)型:所有像int、float、bool和string這些類(lèi)型都屬于值類(lèi)型,使用這些類(lèi)型的變量直接指向存在內(nèi)存中的值,值類(lèi)型的變量的值存儲(chǔ)在棧中。當(dāng)使用等號(hào)=將一個(gè)變量的值賦給另一個(gè)變量時(shí),如 j = i ,實(shí)際上是在內(nèi)存中將 i 的值進(jìn)行了拷貝。可以通過(guò) &i 獲取變量 i 的內(nèi)存地址
指針類(lèi)型:簡(jiǎn)單地說(shuō)go語(yǔ)言的指針類(lèi)型和C/C++的指針類(lèi)型用法是一樣的,除了出去安全性的考慮,go語(yǔ)言增加了一些限制,包括如下幾條:
- 不同類(lèi)型的指針不能互相轉(zhuǎn)化,例如*int, int32, 以及int64
- 任何普通指針類(lèi)型*T和uintptr之間不能互相轉(zhuǎn)化
- 指針變量不能進(jìn)行運(yùn)算, 比如C/C++里面的++, --運(yùn)算
下面將給大家詳細(xì)介紹golang中值類(lèi)型/指針類(lèi)型的變量的一些區(qū)別,下面話不多說(shuō)了,來(lái)一起看看詳細(xì)的介紹吧。
值類(lèi)型的變量和指針類(lèi)型的變量
先聲明一個(gè)結(jié)構(gòu)體:
type T struct {
Name string
}
func (t T) M1() {
t.Name = "name1"
}
func (t *T) M2() {
t.Name = "name2"
}
M1() 的接收者是值類(lèi)型 T, M2() 的接收者是值類(lèi)型 *T , 兩個(gè)方法內(nèi)都是改變Name值。
下面聲明一個(gè) T 類(lèi)型的變量,并調(diào)用 M1() 和 M2() 。
t1 := T{"t1"}
fmt.Println("M1調(diào)用前:", t1.Name)
t1.M1()
fmt.Println("M1調(diào)用后:", t1.Name)
fmt.Println("M2調(diào)用前:", t1.Name)
t1.M2()
fmt.Println("M2調(diào)用后:", t1.Name)
輸出結(jié)果為:
M1調(diào)用前: t1
M1調(diào)用后: t1
M2調(diào)用前: t1
M2調(diào)用后: name2
下面猜測(cè)一下go會(huì)怎么處理。
先來(lái)約定一下:接收者可以看作是函數(shù)的第一個(gè)參數(shù),即這樣的: func M1(t T) , func M2(t *T) 。 go不是面向?qū)ο蟮恼Z(yǔ)言,所以用那種看起來(lái)像面向?qū)ο蟮恼Z(yǔ)法來(lái)理解可能有偏差。
當(dāng)調(diào)用 t1.M1() 時(shí)相當(dāng)于 M1(t1) ,實(shí)參和行參都是類(lèi)型 T,可以接受。此時(shí)在M1()中的t只是t1的值拷貝,所以M1()的修改影響不到t1。
當(dāng)調(diào)用 t1.M2() => M2(t1) ,這是將 T 類(lèi)型傳給了 *T 類(lèi)型,go可能會(huì)取 t1 的地址傳進(jìn)去: M2(&t1) 。所以 M2() 的修改可以影響 t1 。
類(lèi)型的變量這兩個(gè)方法都是擁有的。
下面聲明一個(gè) *T 類(lèi)型的變量,并調(diào)用 M1() 和 M2() 。
t2 := &T{"t2"}
fmt.Println("M1調(diào)用前:", t2.Name)
t2.M1()
fmt.Println("M1調(diào)用后:", t2.Name)
fmt.Println("M2調(diào)用前:", t2.Name)
t2.M2()
fmt.Println("M2調(diào)用后:", t2.Name)
輸出結(jié)果為:
M1調(diào)用前: t2
M1調(diào)用后: t2
M2調(diào)用前: t2
M2調(diào)用后: name2
t2.M1() => M1(t2) , t2 是指針類(lèi)型, 取 t2 的值并拷貝一份傳給 M1。
t2.M2() => M2(t2) ,都是指針類(lèi)型,不需要轉(zhuǎn)換。
*T 類(lèi)型的變量也是擁有這兩個(gè)方法的。
傳給接口會(huì)怎樣?
先聲明一個(gè)接口
type Intf interface {
M1()
M2()
}
使用:
var t1 T = T{"t1"}
t1.M1()
t1.M2()
var t2 Intf = t1
t2.M1()
t2.M2()
報(bào)錯(cuò):
./main.go:9: cannot use t1 (type T) as type Intf in assignment:
T does not implement Intf (M2 method has pointer receiver)
var t2 Intf = t1 這一行報(bào)錯(cuò)。
t1 是有 M2() 方法的,但是為什么傳給 t2 時(shí)傳不過(guò)去呢?
簡(jiǎn)單來(lái)說(shuō),按照接口的理論:傳過(guò)去【賦值】的對(duì)象必須實(shí)現(xiàn)了接口要求的方法,而t1沒(méi)有實(shí)現(xiàn)M2() ,t1的指針實(shí)現(xiàn)了M2() 。另外和c語(yǔ)言一樣,函數(shù)名本身就是指針
當(dāng)把 var t2 Intf = t1 修改為 var t2 Intf = &t1 時(shí)編譯通過(guò),此時(shí) t2 獲得的是 t1 的地址, t2.M2() 的修改可以影響到 t1 了。
如果聲明一個(gè)方法 func f(t Intf) , 參數(shù)的傳遞和上面的直接賦值是一樣的情況。
嵌套類(lèi)型
聲明一個(gè)類(lèi)型 S,將 T 嵌入進(jìn)去
type S struct { T }
使用下面的例子測(cè)試一下:
t1 := T{"t1"}
s := S{t1}
fmt.Println("M1調(diào)用前:", s.Name)
s.M1()
fmt.Println("M1調(diào)用后:", s.Name)
fmt.Println("M2調(diào)用前:", s.Name)
s.M2()
fmt.Println("M2調(diào)用后:", s.Name)
fmt.Println(t1.Name)
輸出:
M1調(diào)用前: t1
M1調(diào)用后: t1
M2調(diào)用前: t1
M2調(diào)用后: name2
t1
將 T 嵌入 S, 那么 T 擁有的方法和屬性 S 也是擁有的,但是接收者卻不是 S 而是 T。
所以 s.M1() 相當(dāng)于 M1(t1) 而不是 M1(s) 。
最后 t1 的值沒(méi)有改變,因?yàn)槲覀兦度氲氖?T 類(lèi)型,所以 S{t1} 的時(shí)候是將 t1 拷貝了一份。
假如我們將 s 賦值給 Intf 接口會(huì)怎么樣呢?
var intf Intf = s intf.M1() intf.M2()
報(bào)錯(cuò):
cannot use s (type S) as type Intf in assignment: S does not implement Intf (M2 method has pointer receiver)
還是 M2() 的問(wèn)題,因?yàn)?s 此時(shí)還是值類(lèi)型。
var intf Intf = &s 這樣的話編譯通過(guò)了,如果在 intf.M2() 中改變了 Name 的值, s.Name 被改變了,但是 t1.Name 依然沒(méi)變,因?yàn)楝F(xiàn)在 t1 和 s 已經(jīng)沒(méi)有聯(lián)系了。
下面嵌入 *T 試試:
type S struct { *T }
使用時(shí)這樣:
t1 := T{"t1"}
s := S{&t1}
fmt.Println("M1調(diào)用前:", s.Name)
s.M1()
fmt.Println("M1調(diào)用后:", s.Name)
fmt.Println("M2調(diào)用前:", s.Name)
s.M2()
fmt.Println("M2調(diào)用后:", s.Name)
fmt.Println(t1.Name)
M1調(diào)用前: t1
M1調(diào)用后: t1
M2調(diào)用前: t1
M2調(diào)用后: name2
name2
惟一的區(qū)別是最后 t1 的值變了,因?yàn)槲覀儚?fù)制的是指針。
接著賦值給接口試試:
var intf Intf = s i ntf.M1() intf.M2() fmt.Println(s.Name)
編譯沒(méi)有報(bào)錯(cuò)。這里我們傳遞給 intf 的是值類(lèi)型而不是指針,為什么可以通過(guò)呢?
拷貝 s 的時(shí)候里面的 T 是指針類(lèi)型,所以調(diào)用 M2() 的時(shí)候傳遞進(jìn)去的是一個(gè)指針。
var intf Intf = &s 的效果和上面一樣。
總結(jié)
以上就是這篇文章的全部?jī)?nèi)容了,希望本文的內(nèi)容對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,如果有疑問(wèn)大家可以留言交流,謝謝大家對(duì)腳本之家的支持。
相關(guān)文章
Go 類(lèi)型轉(zhuǎn)換工具包strconv包的用法
Go 語(yǔ)言的?strconv?包提供了用于基本數(shù)據(jù)類(lèi)型之間轉(zhuǎn)換的函數(shù),本文主要介紹了Go 類(lèi)型轉(zhuǎn)換工具包strconv包的用法,具有一定的參考價(jià)值,感興趣的可以了解一下2024-05-05
golang函數(shù)的返回值實(shí)現(xiàn)
本文主要介紹了golang函數(shù)的返回值實(shí)現(xiàn),文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2023-03-03
解決Goland 提示 Unresolved reference 錯(cuò)誤的問(wèn)題
這篇文章主要介紹了解決Goland 提示 Unresolved reference 錯(cuò)誤的問(wèn)題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧2020-12-12
如何解析golang中Context在HTTP服務(wù)中的角色
這篇文章主要介紹了如何解析golang中Context在HTTP服務(wù)中的角色問(wèn)題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2024-03-03
GO中的時(shí)間操作總結(jié)(time&dateparse)
日常開(kāi)發(fā)過(guò)程中,對(duì)于時(shí)間的操作可謂是無(wú)處不在,但是想實(shí)現(xiàn)時(shí)間自由還是不簡(jiǎn)單的,多種時(shí)間格式容易混淆,本文為大家整理了一下GO中的時(shí)間操作,有需要的可以參考下2023-09-09

