這是一個建立於 的文章,其中的資訊可能已經有所發展或是發生改變。
之前關於Go的複合類型聊到數組和切片,今天繼續看看指標(pointer)和結構體(struct)。
指標(pointer)
取地址符–&
Go具有指標。指標儲存了變數的記憶體位址。
我們都知道,變數是一種使用方便的預留位置,用於引用電腦記憶體位址。
這裡先要說下Go語言的取地址符– &,放到一個變數前使用就會返回相應變數的記憶體位址。
package mainimport "fmt"func main() { var a int = 20 var s string = "a" fmt.Printf("a 變數的儲存地址: %x\n", &a) fmt.Printf("s 變數的儲存地址: %x\n", &s)}
%x是十六進位輸出。
可以看出,兩次啟動並執行結果中,int型的變數地址不同的,string型的變數地址是相同的,這點是不是和java一樣呢?–int是實值型別,string是參考型別。
指標的定義
類型 *T 是指向 T 類型值的指標。其零值為 nil 。
var p *int
以上定義了一個int類型的指標。
& 操作符會產生一個指向其運算元的指標。
* 操作符表示指標指向的底層值。
package mainimport "fmt"func main() { var p *int //定義一個指標變數 fmt.Printf("p 變數的初始儲存地址: %x\n", p) i := 10 p = &i //將指標指向變數i fmt.Printf("p 變數的儲存地址: %x\n", p) /* 使用指標訪問值 */ fmt.Printf("*p 變數的值: %d\n", *p) fmt.Printf("i 變數的值: %d\n", i) *p = 20 //通過指標 p 設定 i fmt.Printf("p 變數的儲存地址: %x\n", p) /* 使用指標訪問值 */ fmt.Printf("*p 變數的值: %d\n", *p) fmt.Printf("i 變數的值: %d\n", i)}
可以看出,
- 指正定義是預設的地址是0,這時它的值就是nil。
- 當指標指向變數i時,它的地址就變成了變數i的地址,通過&操作符完成,這時他的值就是變數i的值。
- 當通過操作符*改變指標的值時,對應的i的值也發生了變化,以為他們引用的是同一個地址上的值。
這也就是通常所說的“間接引用”或“重新導向”。
總結一下,使用指標需要三步,首先,定義指標變數。然後,為指標變數賦值(&)。最後就可以訪問指標變數中指向地址的值(*)。
指標的指標
如果一個指標變數存放的又是另一個指標變數的地址,則稱這個指標變數為指向指標的指標變數。當定義一個指向指標的指標變數時,第一個指標存放第二個指標的地址,第二個指標存放變數的地址。
聲明格式如下:
var ptr **int;
以上指向指標的指標變數為整型。
package mainimport "fmt"func main() { var a int var ptr *int var pptr **int a = 10 /* 指標 ptr 地址 */ ptr = &a /* 指向指標 ptr 地址 */ pptr = &ptr /* 擷取 pptr 的值 */ fmt.Printf("變數 a = %d\n", a) fmt.Printf("變數 a 記憶體位址 %x\n", &a) fmt.Printf("指標變數 *ptr = %d\n", *ptr) fmt.Printf("指標變數 *ptr 記憶體位址 %x\n", &*ptr) fmt.Printf("指向指標的指標變數 **pptr = %d\n", **pptr) fmt.Printf("指向指標的指標變數 **pptr 記憶體位址 %x\n", &**pptr)}
可以看出,變數a,ptr,pptr都指向了同一個地址。實際上他們引用的是同一個記憶體位址,所以他們的值也都是一樣的。
總之,指標在Go中的用途還是很廣泛的,大部分同C語言的也有些共通指出,但與 C 不同,Go 沒有指標運算。
結構體(struct)
結構體( struct )就是由一系列具有相同類型或不同類型的資料構成的欄位集合。在結構體中可以為不同項定義不同的資料類型。
定義和初始化
struct關鍵之用於定義結構體,type 聲明就是定義的類型。
type T struct { name string // name of the object value int // its value}
如下的代碼:
package mainimport "fmt"type Vertex struct { X int Y string}func main() { var v = Vertex{1, "s"} fmt.Println(v) fmt.Println(v.X) fmt.Println(v.Y) v.X = 10 fmt.Println(v.X)}
上面的代碼定義了一個名為Vertex的結構體,結構體是由int和string兩個資料類型構成。
然後在main函數中定義了一個類型為Vertex的變數。通過上面的程式也可以得出,操作一個結構體的內容直接通過.操作符就可以了,不管是取值還是賦值。
結構體指標
結構體指標就是指向結構體的指標,類似於其他指標變數。定義結構體指標,結構體的欄位就可以通過結構體指標來訪問。
特殊的首碼 & 返回一個指向結構體的指標。
如果我們有一個指向結構體的指標 p ,那麼可以通過 (*p).X 來訪問其欄位 X 。 不過這麼寫太囉嗦了,所以語言也允許我們使用隱式間接引用,直接寫 p.X 就可以。
package mainimport "fmt"type Vertex struct { X int Y string}func main() { v := Vertex{1, "2"} p := &v p.X = 333333333 fmt.Println(v) fmt.Println(p.X) fmt.Println((*p).X)}
可以看出,定義的指向結構體v的指標p,對於p的修改就是修改了之前的結構體v。