go語言中指標有哪些運算

2023-01-04 14:02:03

go語言沒有指標運算。go語言的語法上是不支援指標運算的,所有指標都在可控的一個範圍內使用;但實際上,go語言可以通過unsafe包的Pointer()方法把指標轉換為uintptr型別的數位,來間接實現指標運算。

本教學操作環境:windows7系統、GO 1.18版本、Dell G3電腦。

到底什麼是指標呢?

記憶體就是一系列有序列號的儲存單元,變數就是編譯器為記憶體地址分配的暱稱,那麼指標是什麼呢?

指標就是一個指向另一個記憶體地址變數的值

指標指向變數的記憶體地址,指標就像該變數值的記憶體地址一樣

我們來看一個程式碼片段

func main() {
    a := 200
    b := &a
    *b++
    fmt.Println(a)
}
登入後複製

在 main 函數的第一行,我們定義了一個新的變數 a ,並賦值為 200。接下來我們定義了一個變數 b ,並將變數 a 的地址賦值給 b 。我們並不知道 a 的準確儲存地址,但是我們依然可以將 a 的地址儲存在變數 b 中。

1.png

2.png

因為 Go 強型別的特性,第三行程式碼也許是最具干擾性的了,b 包含 a 變數的地址,但是我們想增加儲存在 a 變數中的值。

這樣我們必須取消參照 b ,而是跟隨指標由 b 參照 a。
然後我們將該值加 1 後,儲存回 b 中儲存的記憶體地址上。

最後一行列印了 a 的值,可以看到 a 的值已經增加為了 201

3.png

4.png

Go語言中的函數傳參都是值拷貝,當我們想要修改某個變數的時候,我們可以建立一個指向該變數地址的指標變數

區別於C/C++中的指標,Go語言中的指標不能進行偏移和運算,是安全指標

要搞明白Go語言中的指標需要先知道3個概念:指標地址、指標型別和指標取值

指標地址和指標型別

Go語言中的指標操作非常簡單,只需要記住兩個符號:&(取地址)和*(根據地址取值)。

每個變數在執行時都擁有一個地址,這個地址代表變數在記憶體中的位置。Go語言中使用&字元放在變數前面對變數進行「取地址」操作。

取變數指標的語法如下:

ptr := &v    // v的型別為T
登入後複製

其中:

  • v:代表被取地址的變數,型別為T

  • ptr:用於接收地址的變數,ptr的型別就為*T,稱做T的指標型別。*代表指標。

5.png

func main() {
    a := 10
    b := &a
    fmt.Printf("a:%d ptr:%p\n", a, &a) // a:10 ptr:0xc00001a078
    fmt.Printf("b:%p type:%T\n", b, b) // b:0xc00001a078 type:*int
    fmt.Println(&b)                    // 0xc00000e018
}
登入後複製

指標運運算元

1.指標運運算元為左值時,我們可以更新目標物件的狀態;而為右值時則是為了獲取目標的狀態。

func main() {
    x := 10
    var p *int = &x  //獲取地址,儲存到指標變數
    *p += 20        //用指標間接參照,並更新物件
    println(p, *p)  //輸出指標所儲存的地址,以及目標物件
}
登入後複製

輸出:

0xc000040780 30
登入後複製

2.指標型別支援相等運運算元,但不能做加減運算和型別轉換。如果兩個指標指向同一地址,或都為nil,那麼它們相等。

func main() {
    x := 10
    p := &x

    p++   //編譯報錯 invalid operation: p++ (non-numeric type *int)
    var p2 *int = p+1  //invalid operation: p + 1 (mismatched types *int and int)
    p2 = &x
    println(p == p2)   //指向同一地址
}
登入後複製

可通過unsafe.Pointer將指標轉換為uintptr後進行加減法運算,但可能會造成非法存取。

指標運算

在很多 golang 程式中,雖然用到了指標,但是並不會對指標進行加減運算,這和 C 程式是很不一樣的。Golang 的官方入門學習工具() 甚至說 Go 不支援指標算術。雖然實際上並不是這樣的,但我在一般的 go 程式中,好像確實沒見過指標運算(嗯,我知道你想寫不一般的程式)。

  • 但實際上,go 可以通過 unsafe.Pointer 來把指標轉換為 uintptr 型別的數位,來間接實現指標運算。
  • 這裡請注意,uintptr 是一種整數型別,而不是指標型別。

比如:

uintptr(unsafe.Pointer(&p)) + 1
登入後複製

就得到了 &p 的下一個位元組的位置。然而,根據 《Go Programming Language》 的提示,我們最好直接把這個計算得到的記憶體地址轉換為指標型別:

unsafe.Pointer(uintptr(unsafe.Pointer(&p) + 1))
登入後複製

因為 go 中是有垃圾回收機制的,如果某種 GC 挪動了目標值的記憶體地址,以整型來儲存的指標數值,就成了無效的值。

同時也要注意,go 中對指標的 + 1,真的就只是指向了下一個位元組,而 C 中 + 1 或者 ++ 考慮了資料型別的長度,會自動指向當前值結尾後的下一個位元組(或者說,有可能就是下一個值的開始)。如果 go 中要想實現同樣的效果,可以使用 unsafe.Sizeof 方法:

unsafe.Pointer(uintptr(unsafe.Pointer(&p) + unsafe.Sizeof(p)))
登入後複製

最後,另外一種常用的指標操作是轉換指標型別。這也可以利用 unsafe 包來實現:

var a int64 = 1
(*int8)(unsafe.Pointer(&a))
登入後複製

如果你沒有遇到過需要轉換指標型別的需求,可以看看,其中構建 IP 協定首部的程式碼,就用到了指標型別轉換。

【相關推薦:Go視訊教學、】

以上就是go語言中指標有哪些運算的詳細內容,更多請關注TW511.COM其它相關文章!