天天看點

go 指針使用【2】

文章目錄

  • ​​1. 前言​​
  • ​​2. 什麼是指針​​
  • ​​3. 如何使用指針​​
  • ​​4. Go 空指針(nil)​​
  • ​​5. Go 指針數組​​

1. 前言

指針概念在 Go 語言中被拆分為兩個核心概念:

  • 類型指針,允許對這個指針類型的資料進行修改。傳遞資料使用指針,而無須拷貝 資料。類型指針不能進行偏移和運算。
  • 切片,由指向起始元素的原始指針、元素數量和容量組成。

受益于這樣的限制和拆分, Go 語言的指針類型變量擁有指針的高效通路,但又不會 發生指針偏移,進而避免非法修改關鍵性資料問題。同時, 垃圾回收也比較容易對不會發 生偏移的指針進行檢索和回收。 切片比原始指針具備更強大的特性, 更為安全。切片發生越界時,運作時會報出岩機,并打出堆枝,而原始指針隻會崩潰。 要明白指針,需要知道幾個概念:​

​指針位址、指針類型和指針取值​

​​。

每個變量在運作時都擁有一個位址,這個位址代表變量在記憶體中的位置。 Go 語言中 使用“&” 操作符放在變量前面對變量進行“取位址”操作。

格式如下:

ptr : = &v

II v 的類型為 T 其中 v代表被取位址的變量,被取位址的V使用 陽變量進行接收,p仕的類型就為川T”, 稱做 T 的指針類型。 “*”代表指針

我們都知道,變量是一種使用友善的占位符,用于引用計算機記憶體位址。

Golang 支援指針類型 T,指針的指針 **T,以及包含包名字首的 .T。

  • 預設值 nil,沒有 NULL 常量。
  • 操作符 “&” (取位址符) 取變量位址
  • “*” (取值符)透過指針通路目标對象。
  • 不支援指針運算,不支援 “->” 運算符,直接用 “.” 通路目标成員。

變量在記憶體中位址:

package main
import "fmt"
func main(){
    var a int =10
    fmt.Printf("變量的位址: %x\n",&a)}
執行以上代碼輸出結果為:

變量的位址: c420012058      

2. 什麼是指針

一個指針變量可以指向任何一個值的記憶體位址,它指向那個值的記憶體位址。

類似于變量和常量,在使用指針前你需要聲明指針。

指針聲明格式如下:

var name *類型      

指針聲明:

package main
var ip *int     /* 指向整型*///聲明一個int值得指針變量
var fp *float32 /* 指向浮點型 */
var sp *string  /* 指向字元串類型 */
func main(){}      

3. 如何使用指針

指針使用流程:

定義指針變量。

為指針變量指派。

通路指針變量中指向位址的值。

在指針類型前面加上 * 号(字首)來擷取指針所指向的内容。

$ cat pointer.go
package main
import "fmt"
func main(){
    var a int =20/* 聲明實際變量 */
    var ip *int    /* 聲明指針變量 */
    ip =&a /* 指針變量的存儲位址 */
    fmt.Printf("a 變量的位址是: %x\n",&a)/* 指針變量的存儲位址 */
    fmt.Printf("ip 變量的存儲位址: %x\n", ip)/* 使用指針通路值 */
    fmt.Printf("*ip 變量的值: %d\n",*ip)}
[root@localhost go]# go run pointer.go
a 變量的位址是: c000082010
ip 變量的存儲位址: c000082010
*ip 變量的值: 20      

直接用指針通路目标對象成員:

package main
import ("fmt")
func main(){
    type data struct{ a int }
    var d = data{1234}
    var p *data
    p =&d
    fmt.Printf("%p, %v\n", p, p.a)// 直接用指針通路目标對象成員,無須轉換。}
輸出結果:

0xc420012058,1234      

不能對指針做加減法等運算。

package main
func main(){
    x :=1234
    p :=&x
    p++}
輸出結果:

./main.go:6:3: invalid operation: p++(non-numeric type *int)      

可以在 ​

​unsafe.Pointer​

​ 和任意類型指針間進 轉換。

cat pointer2.go
package main
import (
      "fmt" 
      "unsafe")
func main(){
    x :=0x12345678
    p := unsafe.Pointer(&x)// *int -> Pointer
    n :=(*[4]byte)(p)// Pointer -> *[4]byte
    for i :=0; i <len(n); i++{
        fmt.Printf("%X ", n[i])}
}
[root@localhost pointer]#  go run pointer2.go
78 56 34 12      

将 Pointer 轉換成 uintptr,可變相實作指針運算。

$ cat pointer3.go
package main
import (
     "fmt"
     "unsafe"
)
func main(){
    d := struct {
        s string
        x int
    }{"abc",100}
    p :=uintptr(unsafe.Pointer(&d))   // *struct -> Pointer -> uintptr
    p += unsafe.Offsetof(d.x)         // uintptr + offset
    p2 := unsafe.Pointer(p)           // uintptr -> Pointer
    px :=(*int)(p2) 
    d.x = 200
    fmt.Printf("%#v\n", px)
    fmt.Printf("%#v\n", d)
}
[root@localhost pointer]#  go run pointer3.go
(*int)(0xc00006af30)
struct { s string; x int }{s:"abc", x:200}      

注意:GC 把 uintptr 當成普通整數對象,它無法阻止 “關聯” 對象被回收。

4. Go 空指針(nil)

當一個指針被定義後沒有配置設定到任何變量時,它的值為 nil。

nil 指針也稱為空指針。

nil在概念上和其它語言的null、None、nil、NULL一樣,都指代零值或空值。

一個指針變量通常縮寫為 ptr。

$ cat pointer4.go
package main
import "fmt"
func main(){
    var ptr *int
    fmt.Printf("ptr 的值為 : %x\n", ptr)}
[root@localhost pointer]#  go run pointer4.go
ptr 的值為 : 0      

空指針判斷:

$ cat pointer5.go
package main
import ("fmt")
func main(){
    var ptr1 *int
    var i int =1
    ptr2 :=&i
    if ptr1 == nil { 
        fmt.Println("prt1 是空指針")
    }
    if ptr2 != nil { 
        fmt.Println("prt2 不是空指針")
    }
}
[root@localhost pointer]# go run pointer5.go
prt1 是空指針
prt2 不是空指針      

5. Go 指針數組

定義了長度為 3 的整型數組:

package main
import "fmt"
const MAX int =3
func main(){
    a :=[MAX]int{10,100,200}
    var i int
    for i =0; i < MAX; i++{
        fmt.Printf("a[%d] = %d\n", i, a[i])}}
 
[root@localhost pointer]# go run pointer6.go
a[0] = 10
a[1] = 100
a[2] = 200      

有一種情況,我們可能需要儲存數組,這樣我們就需要使用到指針。

以下聲明了整型指針數組:

var ptr [MAX]*int;      
$ cat pointer7.go
package main
import "fmt"
const MAX int =3
func main(){
    a :=[]int{9,99,999}
    var i int
    var ptr [MAX]*int
    for i =0; i < MAX; i++{
        ptr[i]=&a[i]/* 整數位址指派給指針數組 */
    }
    for i =0; i < MAX; i++{
        fmt.Printf("a[%d] = %d\n", i,*ptr[i])
    }
}
[root@localhost pointer]# go run pointer7.go
a[0] = 9
a[1] = 99
a[2] = 999