首頁 > 軟體

Go語言基礎學習之指標詳解

2022-12-31 14:01:43

今天來說說 Go 語言基礎中的指標。

Go 語言中指標是很容易學習的,Go 語言中使用指標可以更簡單的執行一些任務。

1. 什麼是指標

Go 語言中,一個指標變數指向了一個值的記憶體地址。和 C、C++ 中的指標不同,Go 語言中的指標不能進行計算和偏移操作。

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

Go 語言中的指標操作非常簡單,只有記住兩個符號就可以了。

  • &(取地址)
  • *(根據地址取值)
var ip *int /* 指向整型*/

畫個重點,我們想徹底搞明白指標,必須要掌握 3 個概念:

  • 指標地址
  • 指標型別
  • 指標取值

接下來我們從這 3 點大家闡述 Go 語言指標,方便大家掌握。

2. 指標地址 & 指標型別

Go 語言變數在執行時都會被指定一個記憶體地址,即變數在記憶體中的位置。Go 語言通常在使用時會在變數前放一個 & 代表對變數進行 “ 取地址 ” 操作。Go 語言常用的值型別 (string、int、array、struct、float、bool )都會有對應的指標型別。如:string、 int、*int64 等。

每個變數在執行時都擁有一個地址,這個地址代表變數在記憶體中的位置。Go語言中使用&字元放在變數前面對變數進行“取地址”操作。 Go語言中的值型別(int、float、bool、string、array、struct)都有對應的指標型別,如:int、int64、*string等。

取變數指標的語法如下:

package main

import "fmt"

func main(){
        
    a := 10   /* 宣告實際變數 */
    ip := &a  /* 指標變數的儲存地址 */
    
    
    fmt.Printf("a 變數的地址是: %xn", &a  )
    
    /* 指標變數的儲存地址 */
    fmt.Printf("ip 變數儲存的指標地址: %xn", ip )
    
    /* 使用指標存取值 */
    fmt.Printf("*ip 變數的值: %dn", *ip )
}

執行結果:

a 變數的地址是: 0xc000010200

ip 變數儲存的指標地址: 0xc000010200

*ip 變數的值: 10

其中:

  • a: 代表被取地址的變數,型別為 int
  • ip: 用於接收地址的變數,ip 的型別就為 *int,稱做 int 的指標型別。*代表指標。

用圖來表示一下 ip := &a:

以上就是指標地址和指標型別。

3. 指標取值

對變數使用 &會獲取該變數的指標,對指標使用 * 會獲取到值,也就是 “指正取值”。舉個例子更好的理解一下:

package main

import "fmt"

func main() {
    
    a := 20   /* 宣告實際變數 */
    b := &a  /* 指標變數的儲存地址 */
    fmt.Printf("type of b:%Tn", &a  )
    
    c := *b // 指標取值(根據指標去記憶體取值)
    fmt.Printf("type of c:%Tn", c)
    fmt.Printf("value of c:%vn", c)
}

控制檯輸出結果:

type of b:*int
type of c:int
value of c:10

小結一下:

  • 指標變數的值是指標地址 (可以結合上圖更好的理解)
  • 對變數進行取地址 &操作,可以獲得這個變數的指標變數
  • 對指標變數進行取值 *操作,可以獲得指標變數指向的原變數的值

函數傳值:

package main

import "fmt"

func main() {
    
    x := 2
    mod1(x)
    fmt.Println(x) // 2
    
    mod2(&x)
    fmt.Println(x) // 1024
}

func mod1(x int) {
    x = 1024
}

func mod2(x *int) {
    *x = 1024
}

4. 空指標

當一個指標被定義後沒有分配到任何變數時,它的值為 nil

nil 指標也稱為空指標。

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

一個指標變數通常縮寫為 ptr

舉個例子:

package main

import "fmt"

func main() {
   var  ptr *int
   fmt.Printf("ptr 的值為 : %xn", ptr) // ptr 的值為 : 0
}

空指標判斷:

package main

import "fmt"

func main() {
    var ptr *string
    fmt.Println(ptr)
    fmt.Printf("ptr的值是%vn", ptr)
    
    if ptr != nil {
        fmt.Println("非空")
    } 
    if ptr == nil {
        fmt.Println("空值")
    }
}

5. make

make 是用於初始化內建的資料結構,比如 slicemap 和 channel

func make(t Type, size ...IntegerType) Type

舉個例子:

package main

import "fmt"

func main() {
    var user map[string]int
    user = make(map[string]int, 10)
    user["age"] = 18
    fmt.Println(user)

    /** 
        slice := make([]int, 0, 100)
        hash := make(map[int]bool, 10)
        ch := make(chan int, 5)
    **/
}

6. new

new 的作用是根據傳入的型別分配一片記憶體空間並返回指向這片記憶體空間的指標。

func new(Type) *Type

解釋一下:

  • Type 表示型別,new 函數只接受一個引數,這個引數是一個型別
  • *Type 表示型別指標,new 函數返回一個指向該型別記憶體地址的指標

舉個例子:

package main

import "fmt"

func main() {
    a := new(int)
    b := new(bool)
    fmt.Printf("%Tn", a) // *int
    fmt.Printf("%Tn", b) // *bool
    fmt.Println(*a)       // 0
    fmt.Println(*b)       // false
}

7. make 和 new 的區別

面試高頻題,這個要考,記一下。

  • make 和 new 都是用來做記憶體分配
  • make 只用於 slice、map 、channel 的初始化,返回的還是這三個參照型別本身,因為這三種型別就是參照型別,所以就沒必要返回其指標了
  • new 用於型別的記憶體分配,並且記憶體對應的值為型別零值,返回的是指向型別的指標。

8. 問題

Q: 執行下面的程式碼會出現啥問題?

package main

import "fmt"

func main() {
    var a *int
    *a = 100
    fmt.Println(*a)

    var user map[string]int
    user["age"] = 18
    fmt.Println(user)
}

A: 會出現 panic runtime error: invalid memory address or nil pointer dereference。出錯行數在第 7 行。

原因:在 Go 語言中我們使用參照型別的變數需要先申明、分配記憶體空間,否則在賦值是會出錯。值型別的變數除外,因為其在申明時就分配了預設的記憶體空間。這也是 new 和 make 的作用。

到此這篇關於Go語言基礎學習之指標詳解的文章就介紹到這了,更多相關Go語言指標內容請搜尋it145.com以前的文章或繼續瀏覽下面的相關文章希望大家以後多多支援it145.com!


IT145.com E-mail:sddin#qq.com