8.Go語言基礎之指針

Go語言中的指針不能進行偏移和運算,是安全指針。
在瞭解GO語言中的指針前,首先需要知道三個概念:指針地址、指針類型和指針取值。

1.Go語言中的指針

任何數據載入內存後,在內存中都有對應的地址,這就是指針。
爲了保存一個數據在內存中的地址,需要指針變量。
比如"好好學習,天天向上"這個字符串寫入程序中,程序一啓動這句話就加載到內存(假設內存地址爲0x123456),在程序中,把該字符串賦值給變量A,把該字符串的內存地址賦值給變量B。
這時變量B就是一個指針變量。通過變量A和變量B都能找到該字符串。
Go語言中的指針不能進行偏移和運算,因此Go語言中的指針操作非常簡單,只需要記住兩個符號:&(取地址)和*(根據地址取值)。
也需要記得:
值類型有:int、float、bool、string、array、struct
引用類型有:指針,map,切片,chan

1.1指針地址和指針類型

每個變量在運行時都擁有一個地址,這個地址代表變量在內存中的位置。
Go語言中使用&字符放在變量前面對變量進行“取地址”操作。
Go語言中的值類型(int,float,bool,string,array,struct)都有對應的指針類型,如*int,*in64,*string等。

取變量指針的語法如下:
ptr := &v    // v的類型爲T

其中:
v:代表被取地址的變量,類型爲T
ptr:用於接收地址的變量,ptr的類型就是*T,稱作T的指針類型。*代表指針。

package main

import "fmt"

func main() {
    a:=10
    b:=&a
    fmt.Printf("a:%d ptr:%p\n",a,&a)
    fmt.Printf("b:%p type:%T\n",b,b)
    fmt.Println(&b)
}
結果:
a:10 ptr:0xc000054080
b:0xc000054080 type:*int
0xc000080018

Process finished with exit code 0

8.Go語言基礎之指針

1.2指針取值

在對普通變量使用&操作符取地址後,會獲得這個變量的指針,然後可以對指針使用*操作,即指針取值。

package main

import "fmt"

func main() {
    a:=10
    b:=&a
    fmt.Printf("type of b:%T\n",b)
    c:=*b
    fmt.Printf("type of c:%T\n",c)
    fmt.Printf("value of c:%v\n",c)
}
結果:
type of b:*int
type of c:int
value of c:10

Process finished with exit code 0
總結:
取地址操作符&和取值操作符*是一對互補操作符,&取出地址,*根據地址取出地址指向的值。
變量、指針地址、指針變量、取地址、取值的相互關係和特性如下:
1.對變量進行取地址(&)操作,可以獲取這個變量的指針變量。
2.指針變量的值是指針地址。
3.對指針變量進行取值(*)操作,可以獲得指針變量指向的原變量的值。
package main

import "fmt"

func modify1(x int)  {
    x = 100
}
func modify2(x *int)  {
    *x = 100
}
func main() {
    a:=10
    modify1(a)
    fmt.Println(a)//10
    modify2(&a)
    fmt.Println(a)//100
}

1.3new和make

先看一個例子:

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

    var b map[string]int
    b["沙河娜扎"] = 100
    fmt.Println(b)
}

結果:
panic: runtime error: invalid memory address or nil pointer dereference
[signal 0xc0000005 code=0x1 addr=0x0 pc=0x494aaa]

goroutine 1 [running]:
main.main()
    C:/Users/Administrator/Desktop/go/src/myproject1/test.go:7 +0x2a

Process finished with exit code 2

上面的代碼引發了panic,在Go語言中,對於引用類型的變量,我們在使用的時候不僅要聲明它,還要爲它分配內存空間,否則我們值就沒辦法存儲。
對於值類型的聲明,不需要分配內存空間,因爲在聲明的時候,已經默認分配好了內存空間。
要分配內存,就引出了new和make,Go語言中使用new和make來分配內存。

1.3.1new

new是一個內置函數,它的函數簽名如下:
func new(Type) *Type
其中,
Type表示類型,new函數只接收一個參數,這個參數是一個類型。
*Type表示指針類型,new函數返回一個指向該類型內存地址的指針。
new函數不太常用,使用new函數得到的是一個類型的指針,並且該指針對應的值爲該類型的零值。

package main

import "fmt"

func main() {
    a:=new(int)
    b:=new(bool)
    fmt.Printf("%T\n",a)//*int
    fmt.Printf("%T\n",b)//*bool
    fmt.Println(*a)//0
    fmt.Println(*b)//false
}
本節開始的示例代碼中,var a *int只是聲明瞭一個指針變量a,但是沒有初始化,指針作爲引用類型,需要初始化後才能擁有內存空間,才能給它賦值。
應該按照如下方式,使用內置的new函數對a進行初始化後纔可以正常進行賦值。

func main() {
    var a *int
    a = new(int)
    *a = 10
    fmt.Println(*a)
}

1.3.2make

make也是用於內存分配的,與new相比,make只用於slice、map及chan的內存創建,而且它的返回類型就是三個類型本身,不是他們的指針類型,
因爲這三種類型就是引用類型,所以沒必要返回指針了。
make函數的函數簽名如下:
func make(t Type, size ...IntegerType) Typ

make函數是無可替代的,我們在使用slice、map、及channel的時候,都要使用make進行初始化,然後纔可以對它們進行操作。channel我們稍後講解。

本節開始的實例中var b map[string]int只是聲明變量b是一個map類型的變量,需要像下面的示例代碼一樣使用make函數進行初始化操作後,才能對其進行鍵值對賦值。

func main() {
    var b map[string]int
    b = make(map[string]int, 10)
    b["沙河娜扎"] = 100
    fmt.Println(b)
}
new與make的區別
1.兩者都是用來做內存分配的。
2.make只用於slice、map及channel的初始化,返回的還是三中類型本身。
3.new用於類型的內存分配,並且內存對應的值爲類型零值,返回的是指向類型的指針。
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章