摘要:指針類型的零值指針類型的零值指針類型的零值都是,也就是說,一個沒有存儲地址的指針等于解除引用解除引用一個指針變量持有另一個變量的地址。
本篇翻譯自《Practical Go Lessons》 Chapter 15: Pointer type
指針是“是一個數(shù)據(jù)項,它存儲另外一個數(shù)據(jù)項的位置”。
在程序中,我們不斷地存儲和檢索數(shù)據(jù)。例如,字符串、數(shù)字、復(fù)雜結(jié)構(gòu)…。在物理層面,數(shù)據(jù)存儲在內(nèi)存中的特定地址,而指針存儲的就是這些特定內(nèi)存地址。
記住指針變量,就像其他變量一樣,它也有一個內(nèi)存地址。
Go 中的指針類型不止一種,每一種普通類型就對應(yīng)一個指針類型。相應(yīng)地,指針類型也限定了它自己只能指向?qū)?yīng)類型的普通變量(地址)。
指針類型的語法為:
*BaseType
BaseType
指代的是任何普通類型。
我們來看一下例子:
*int
表示指向 int
類型的指針*uint8
表示指向 uint8
類型的指針type User struct { ID string Username string}
*User
表示指向 User
類型的指針下面的語法可以創(chuàng)建:
var p *int
這里我們創(chuàng)建了一個類型為 *int
的變量 p
。*int
是指針類型(基礎(chǔ)類型是 int
)。
讓我們來創(chuàng)建一個名為 answer
的整型變量。
var answer int = 42
現(xiàn)在我們給變量 p
分配一個值了:
p = &answer
使用 &
符號我們就能得到變 answer
的地址。來打印出這個地址~
fmt.Println(p)// 0xc000012070
0xc000012070
是一個十六進(jìn)制數(shù)字,因為它的以 0x
為前綴。內(nèi)存地址通常是以十六進(jìn)制格式表示。你也可以使用二進(jìn)制(用 0 和 1)表示,但不易讀。
指針類型的零值都是 nil
,也就是說,一個沒有存儲地址的指針等于 nil
var q *intfmt.Println(q == nil)// true
一個指針變量持有另一個變量的地址。如果你想通過指針去訪問地址背后的變量值該怎么辦?你可以使用解除引用操作符 *
。
來舉個例子,我們定義一個結(jié)構(gòu)體類型 Cart
:
type Cart struct { ID string Paid bool}
然后我們創(chuàng)建一個 Cart
類型的變量 cart
,我們可以得到這個變量的地址,也可以通過地址找到這個變量:
*
操作符,你可以通過地址找到變量值&
操作符,你可以得到變量的地址每個 Go 程序員都會遇到這個 panic(報錯):
panic: runtime error: invalid memory address or nil pointer dereference[signal SIGSEGV: segmentation violation code=0x1 addr=0x0 pc=0x1091507]
為了更好地理解它,我們來復(fù)現(xiàn)一下:
package mainimport "fmt"func main() { var myPointerVar *int fmt.Println(*myPointerVar)}
在程序里,我們的定義了一個指針變量 myPointerVar
,這個變量的類型是 *int
(指向整型)。
然后我嘗試對它進(jìn)行解引用,myPointerVar
變量持有一個尚未初始化的指針,因此該指針的值為 nil
。因為我們嘗試去尋找一個不存在的地址,程序?qū)箦e!我們嘗試找到空地址,而空地址在內(nèi)存中不存在。
Maps 和 channels 變量里保存了對內(nèi)部結(jié)構(gòu)的指針。因此,即便向一個函數(shù)或方法傳遞的 map 或 channel 不是指針類型,也開始對這個 map 或 channel 進(jìn)行修改。讓我們看一個例子:
func addElement(cities map[string]string) { cities["France"] = "Paris"}
package mainimport "log"func main() { cities := make(map[string]string) addElement(cities) log.Println(cities)}
cities
的 mapaddElement
map[France:Paris]
我們將在專門的部分中更廣泛地介紹 channels 和 maps。
切片是相同類型元素的集合。在內(nèi)部,切片是一個具有三個字段的結(jié)構(gòu):
EUcountries
的例子:package mainimport "log"func main() { EUcountries := []string{"Austria", "Belgium", "Bulgaria"} log.Println(EUcountries)}
package mainimport "log"func main() { EUcountries := []string{"Austria", "Belgium", "Bulgaria"} addCountries(EUcountries) log.Println(EUcountries)}func addCountries(countries []string) { countries = append(countries, []string{"Croatia", "Republic of Cyprus", "Czech Republic", "Denmark", "Estonia", "Finland", "France", "Germany", "Greece", "Hungary", "Ireland", "Italy", "Latvia", "Lithuania", "Luxembourg", "Malta", "Netherlands", "Poland", "Portugal", "Romania", "Slovakia", "Slovenia", "Spain", "Sweden"}...)}
addCountries
將一個字符串類型切片作為參數(shù)append
向切片添加字符串來修改切片[Austria Belgium Bulgaria Croatia Republic of Cyprus Czech Republic Denmark Estonia Finland France Germany Greece Hungary Ireland Italy Latvia Lithuania Luxembourg Malta Netherlands Poland Portugal Romania Slovakia Slovenia Spain Sweden][Austria Belgium Bulgaria]
答案:這個函數(shù)實際輸出:
[Austria Belgium Bulgaria]
[]string
類型元素作為參數(shù)EUcountries
拷貝一份傳進(jìn)去讓我們在函數(shù)中添加一個日志來可視化它:
func addCountries(countries []string) { countries = append(countries, []string{"Croatia", "Republic of Cyprus", "Czech Republic", "Denmark", "Estonia", "Finland", "France", "Germany", "Greece", "Hungary", "Ireland", "Italy", "Latvia", "Lithuania", "Luxembourg", "Malta", "Netherlands", "Poland", "Portugal", "Romania", "Slovakia", "Slovenia", "Spain", "Sweden"}...) log.Println(countries)}
日志打印出:
[Austria Belgium Bulgaria Croatia Republic of Cyprus Czech Republic Denmark Estonia Finland France Germany Greece Hungary Ireland Italy Latvia Lithuania Luxembourg Malta Netherlands Poland Portugal Romania Slovakia Slovenia Spain Sweden]
package mainimport ( "log" "strings")func main() { EUcountries := []string{"Austria", "Belgium", "Bulgaria"} upper(EUcountries) log.Println(EUcountries)}func upper(countries []string) { for k, _ := range countries { countries[k] = strings.ToUpper(countries[k]) }}
upper
,它將把一個字符串切片的每個元素都轉(zhuǎn)換成大寫問題:依你看,程序?qū)鬏斚旅婺膫€?
[AUSTRIA BELGIUM BULGARIA][Austria Belgium Bulgaria]
答案:這個函數(shù)將返回:
[AUSTRIA BELGIUM BULGARIA]
upper
獲取切片 EUcountries 的副本(和上面一樣)countries[k] = strings.ToUpper(countries[k])
如果使用切片指針,你就可以在函數(shù)中修改這個切片了:
package mainimport ( "log")func main() { EUcountries := []string{"Austria", "Belgium", "Bulgaria"} addCountries2(&EUcountries) log.Println(EUcountries)}func addCountries2(countriesPtr *[]string) { *countriesPtr = append(*countriesPtr, []string{"Croatia", "Republic of Cyprus", "Czech Republic", "Denmark", "Estonia", "Finland", "France", "Germany", "Greece", "Hungary", "Ireland", "Italy", "Latvia", "Lithuania", "Luxembourg", "Malta", "Netherlands", "Poland", "Portugal", "Romania", "Slovakia", "Slovenia", "Spain", "Sweden"}...)}
這個程序?qū)⑤敵觯?/p>
[Austria Belgium Bulgaria Croatia Republic of Cyprus Czech Republic Denmark Estonia Finland France Germany Greece Hungary Ireland Italy Latvia Lithuania Luxembourg Malta Netherlands Poland Portugal Romania Slovakia Slovenia Spain Sweden]
addCountries2
將字符串切片的指針([]string
)作為參數(shù)append
調(diào)用時的第一個參數(shù)是 *countriesPtr
(即我們通過指針 countriesPtr
去找到原值)append
的第二個參數(shù)沒有改變addCountries2
的結(jié)果會影響到外部的變量有一個快捷方式可以讓你直接修改 struct 類型的變量而無需使用*
運(yùn)算符:
type Item struct { SKU string Quantity int}type Cart struct { ID string CreatedDate time.Time Items Item}cart := Cart{ ID: "115552221", CreatedDate: time.Now(),}cartPtr := &cartcartPtr.Items = []Item{ {SKU: "154550", Quantity: 12}, {SKU: "DTY8755", Quantity: 1},}log.Println(cart.Items)// [{154550 12} {DTY8755 1}]
cart
是一個 Cart
類型變量cartPtr := &cart
會獲取變量 cart 的地址然后將其存儲到 cartPtr
中cartPtr
,我們可以直接修改變量 cart
的 Item
字段(*carPtr).Items = []Item{ {SKU: "154550", Quantity: 12}, {SKU: "DTY8755", Quantity: 1},}
(這也有效,但更冗長)
指針通常用作方法的接收器,讓我們以 Cat
類型為例:
type Cat struct { Color string Age uint8 Name string}
你可以定義一個方法,使用指向 Cat
的指針作為方法的接收器(*Cat
):
func (cat *Cat) Meow(){ fmt.Println("Meooooow")}
Meow
方法沒有做任何有實際意義的事嗎;它只是打印了字符串"Meooooow"
。我們沒有修改比變量的值。我們來看另一個方法,它修改了 cat 的 Name
:
func (cat *Cat) Rename(newName string){ cat.Name = newName}
此方法將更改貓的名稱。通過指針,我們修改了 Cat 結(jié)構(gòu)體的一個字段。
當(dāng)然,如果你不想使用指針作為接收器,你也可以:
func (cat Cat) RenameV2(newName string){ cat.Name = newName}
在這個例子中,變量 cat
是一個副本。接收器被命名為“值接收器”。因此,你對 cat 變量所做的任何修改都將在 cat 副本上完成:
package mainimport "fmt"type Cat struct { Color string Age uint8 Name string}func (cat *Cat) Meow() { fmt.Println("Meooooow")}func (cat *Cat) Rename(newName string) { cat.Name = newName}func (cat Cat) RenameV2(newName string) { cat.Name = newName}func main() { cat := Cat{Color: "blue", Age: 8, Name: "Milow"} cat.Rename("Bob") fmt.Println(cat.Name) // Bob cat.RenameV2("Ben") fmt.Println(cat.Name) // Bob}
在主函數(shù)的第一行,我們創(chuàng)建了一個 Cat
類型的變量 cat,它的 Name 是 "Millow"
。
當(dāng)我們調(diào)用具有值接收器的 RenameV2
方法時,函數(shù)外部變量 cat 的 Name 沒有發(fā)生改變。
當(dāng)我們調(diào)用 Rename
方法時,cat 的 Name 字段值會發(fā)生變化。
Product
指針的變量?Product
指針的變量?*Product
*
*T
表示所有指向 T
類型變量的指針集合&
。它將獲取一個變量的地址userId := 12546584p := &userId
`userId` 是 `int` 類型的變量`p` 是 `*int` 類型變量`*int` 表示所有指向 `int` 類型變量的指針
*[]string
)*
userId := 12546584p := &userId*p = 4log.Println(userId)
p
是一個指針
- 我們使用
*p
來對指針p
進(jìn)行解引用- 我們用指令
*p = 4
修改userId
的值- 在代碼片段的末尾,userId 的值為 4(不再是 12546584)
type Cart struct { ID string}var cart CartcartPtr := &cart
- 不需要這樣寫:
(*cartPtr).ID = "1234"
- 你可直接這樣寫:
cartPtr.Items = "1234"
- 變量
cart
就會被修改
文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請注明本文地址:http://systransis.cn/yun/125672.html
摘要:在包下主要包括輸入輸出兩種流,每種輸入輸出流又可分為字節(jié)流和字符流兩大類。輸入輸出是從程序運(yùn)行所在的內(nèi)存的角度而言的。的輸入流主要由和作為基類,而輸出流主要由和作為基類。 本章主要參考和摘自瘋狂java講義上面的(java編程思想的后面看過后有新的內(nèi)容再補(bǔ)充進(jìn)去吧)?! ≥斎胼敵鍪撬谐绦蚨急匦璧牟糠帧褂幂斎霗C(jī)制允許程序讀取外部數(shù)據(jù)(包括磁盤、光盤等存儲設(shè)備上的數(shù)據(jù)和用戶輸入的...
摘要:基于版本基于版本。由于中英行文差異,完全的逐字逐句翻譯會很冗余啰嗦。譯者在翻譯中同時參考了谷歌百度有道翻譯的譯文以及編程思想第四版中文版的部分內(nèi)容對其翻譯死板,生造名詞,語言精煉度差問題進(jìn)行規(guī)避和改正。 來源:LingCoder/OnJava8 主譯: LingCoder 參譯: LortSir 校對:nickChenyx E-mail: 本書原作者為 [美] Bru...
摘要:是目前最熱門的一種前端開發(fā)框架。對于前端工程師來說,掌握這門炙手可熱的技術(shù)是完全有必要的。雖然目前已出,但是官方并不會放棄版本,還會持續(xù)維護(hù)更新,而且掌握的基本知識能更快的幫助我們邁入。 AngularJS是目前最熱門的一種前端開發(fā)框架。對于前端工程師來說,掌握這門炙手可熱的技術(shù)是完全有必要的。本書會將作者掌握的AngularJS知識傾囊相授,并從學(xué)以致用的角度出發(fā),用實例詳細(xì)地講解各...
閱讀 3799·2023-01-11 11:02
閱讀 4305·2023-01-11 11:02
閱讀 3126·2023-01-11 11:02
閱讀 5237·2023-01-11 11:02
閱讀 4800·2023-01-11 11:02
閱讀 5573·2023-01-11 11:02
閱讀 5376·2023-01-11 11:02
閱讀 4079·2023-01-11 11:02