之前關於Go的複合類型聊到數組和切片,今天繼續看看指針(pointer)和結構體(struct)。
指針(pointer)
取地址符–&
Go具有指針。指針保存了變量的內存地址。
我們都知道,變量是一種使用方便的佔位符,用於引用計算機內存地址。
這裏先要說下Go語言的取地址符– &,放到一個變量前使用就會返回相應變量的內存地址。
package main
import "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 main
import "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 main
import "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 main
import "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 main
import "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。