Golang的高級數據類型-指針(Pointer)實戰篇


          Golang的高級數據類型-指針(Pointer)實戰篇

                               作者:尹正傑

版權聲明:原創作品,謝絕轉載!否則將追究法律責任。

 

 

  前面分享過存儲數據的方式,可以通過變量,或者復合類型中的數組,切片,Map,結構體等進行存儲。今天我們來分享一下Go語言中的指針操作。

  

一.變量地址和指針

1>.定義指針變量

package main

import (
    "fmt"
)

func main() {
    /**
    在Go語言中,一旦定義了一個變量就會在內存中開辟空間。因此,在Go語言中為了避免空間的浪費,定義變量若未使用就會編譯報錯。
    */
    var age uint8 = 18
    /**
    我們可以使用取地址運算符("&")來獲取數據的內存地址。
    %p:
        是一個占位符,表示輸出一個十六進制地址格式。
    \n:
        表示換行符。
    */
    fmt.Printf("age的值是:[%d],age的內存地址是:[%p]\n", age, &age)

    /*
        接下來我們定義一個uint8類型的指針變量p1,其實指針變量也是變量,只不過指針變量指向了變量的內存地址。
    */
    var p1 *uint8

    /*
        對age變量取地址並將結果賦值給指針變量。
    */
    p1 = &age
    fmt.Println(p1)
}

2>.數組和切片指針的區別

package main

import (
    "fmt"
)

func main() {
    //定義一個字符串數組
    var nameList [56]string
    fmt.Printf("nameLists數組的內存地址為%p\n", &nameList)
    fmt.Printf("nameList數組中第一個元素的地址為%p\n", &nameList[0])
    fmt.Printf("nameList數組中第二個元素的地址為%p\n", &nameList[1])
    fmt.Printf("nameList數組中第三個元素的地址為%p\n\n", &nameList[2])

    //定義一個整型數組
    var age_list [56]int
    fmt.Printf("age_list數組的內存地址為%p\n", &age_list)
    fmt.Printf("age_list數組中第一個元素的地址為%p\n", &age_list[0])
    fmt.Printf("age_list數組中第二個元素的地址為%p\n", &age_list[1])
    fmt.Printf("age_list數組中第三個元素的地址為%p\n\n", &age_list[2])

    //定義一個整型切片
    age_slice := make([]int, 56)
    /**
    age_slice切片變量保存的是存儲切片容器的地址。他並不像數組那樣直接指向數組的首元素地址,它類似於一個二級指針。
    當我們使用取地址運算符對age_slice變量取地址時並不是取的切片真正存儲切片容器的地址,而是存儲切片容器地址變量本身的地址喲~
    */
    fmt.Printf("age_slice切片變量本身的內存地址為%p\n", &age_slice)

    /**
    通過下標訪問第一個元素其實訪問的是age_slice變量中保存的內存地址對應的下標。
    */
    fmt.Printf("age_slice切片中第一個元素的地址為%p\n", &age_slice[0])
    fmt.Printf("age_slice切片中第二個元素的地址為%p\n", &age_slice[1])
    fmt.Printf("age_slice切片中第三個元素的地址為%p\n\n", &age_slice[2])
}

 

二.指針的使用

1>.通過指針間接修改變量的值

package main

import (
    "fmt"
)

func main() {

    //使用自動推導類型創建一個age變量
    age := 18

    //我們將age變量的內存地址賦值給指針變量p1
    p1 := &age

    /**
    我們可以使用取值運算符("*")來獲取指針變量的值。
    %p:
        是一個占位符,表示輸出一個十六進制地址格式。
    %d:
        是一個占位符,表示輸出一個整型。
    \n:
        表示換行符。
    *:
        取值運算符,可以將指針變量中的保存的數據取出來。換句話說,可以將一段內存地址保存的值取出來。
    */
    fmt.Printf("age的內存地址是:%p,age的值是:%d\n", p1, *p1)

    //我們可以通過指針間接修改變量的值
    *p1 = 27
    fmt.Printf("age的內存地址是:%p,age的值是:%d\n", p1, age)
}

2>.使用指針注意事項

package main

import (
    "fmt"
)

func main() {

    /**
    溫馨提示:
        在使用指針變量時,要注意定義指針默認值為nil,如果直接擦歐總指向nil的內存地址會報錯;
        在其它語言中,比如c++,指針是允許運算的,但是在Go語言中,指針只有取地址運算符和取值運算符,不允許指針參與運算喲~
    */
    var p *int

    /**
    錯誤代碼:
        由於p指針變量默認值是nil,因此會報錯"invalid memory address or nil pointer dereference"
    */
    //*p = 123

    age := 18
    p = &age //所以,在使用指針變量時,一定要讓指針變量有正確的指向。
    fmt.Printf("age的內存地址是:%p,age的值是:%d\n", p, *p)

    /**
    錯誤代碼:
        在Go語言中,指針只有取地址運算符和取值運算符。
    */
    //p2 := p + 100
}

 

三.指針變量賦值

package main

import (
    "fmt"
)

func main() {
    /**
    為指針賦值有兩種方法:
        1>.把同類型的變量地址賦值給指針;
        2>.使用new函數,就是申請一片內存空間,返回地址;
    */
    var (
        p1 *int
        p2 *int
    )
    fmt.Println("p1的默認值是:", p1)
    fmt.Println("p2的默認值是:", p2)

    /**
    方案一:
        定義一個同類型的變量,然后取地址賦值給指針變量。
    */
    age := 18
    p1 = &age
    fmt.Printf("p1的內存地址是:%p,p1的值是:%d\n", p1, *p1)

    /**
    方案二:
        使用new函數,比如"new(int)"表示創建一個int大小的內存空間,返回為*int
    */
    p2 = new(int)
    fmt.Printf("p2的內存地址是:%p,p2的值是:%d\n", p2, *p2)
    *p2 = 2020 //為指針變量賦值
    fmt.Printf("p2的內存地址是:%p,p2的值是:%d\n", p2, *p2)
}

 

四.指針的應用場景(指針變量作為函數參數傳遞的時候是引用傳遞)

1>.交換兩個變量的值案例

package main

import "fmt"

/*
    定義一個函數交換2個變量的值,接收的是2個int變量
*/
func swap(x int, y int) {
    x, y = y, x
}

/*
    定義一個函數交換2個變量的值,接收的是2個int指針變量
*/
func swapPointer(x *int, y *int) {
    *x, *y = *y, *x //需要改變的是地址上存儲的內容
}

func main() {
    /*
        定義2個變量,一會用於在函數中交換兩個變量的值
    */
    var (
        a = 100
        b = 200
    )

    /*
        值傳遞,不能通過函數內部修改影響外部變量。
    */
    swap(a, b)
    fmt.Printf("a = [%d], b = [%d]\n", a, b)

    /*
        指針變量作為函數參數傳遞的時候是引用傳遞
    */
    swapPointer(&a, &b)
    fmt.Printf("a = [%d], b = [%d]\n", a, b)
}

2>.結構體傳參案例

package main

import (
    "fmt"
)

type Student struct {
    Name string
    Age  int
}

func addAge(s Student) {
    s.Age += 1
}

/**
在實際開發中,一般傳遞的結構體對象都是指針傳遞
*/
func addAgePointer(s *Student) {
    s.Age += 2
}

func main() {

    s1 := Student{"Jason Yin", 18}
    addAge(s1)
    fmt.Printf("姓名:[%s],年齡[%d]\n", s1.Name, s1.Age)
    /**
    結構體對象是值傳遞,但一般函數傳遞的都是結構體對象指針
    */
    addAgePointer(&s1)
    fmt.Printf("姓名:[%s],年齡[%d]\n", s1.Name, s1.Age)
}

 

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM