Go語言指針
文章目錄
- Go語言指針
- 一、指針
- 1.1、Go語言中的指針
- 1.1.1、指針地址和指針類型
- 1.1.2、指針取值
- 1.1.3、空指針
- 1.1.4、new和make
- 1.1.5、new
- 1.1.6、make
- 1.1.7、new與make的區別
一、指針
- 區別于C/C++中的指針,Go語言中的指針不能進行偏移和運算,是安全指針。
- 要搞明白Go語言的指針需要先知道3個概念:指針地址、指針類型、指針取值。
1.1、Go語言中的指針
- Go語言中的函數傳參都是值拷貝,當我們想要修改某個變量的時候,我們可以創建一個指向該變量地址的指針變量。傳遞數據使用指針,而無需拷貝數據。類型指針不能進行偏移和運算。Go語言中的指針操作非常簡單,只需要記住兩個符號:
&
(取地址)和*
(根據地址取值)。
1.1.1、指針地址和指針類型
-
每個變量在運行時都擁有一個內存地址,這個地址代表變量在內存中的位置。Go語言中使用
&
字符放在變量前面對變量進行"取內存地址"操作。Go語言中的值類型(數據類型)(int、float、bool、string、array、struct
)都有對應的指針類型,如:(*int、*int64、*string
)等。 -
取變量指針的語法如下:
ptr := &v // v的類型為T
- 其中:
v: 代表取地址的變量,類型為T
ptr: 用于接收地址的變量,ptr的類型就為*T,稱做T的指針類型。*代表指針
- 舉個例子:
package mainimport "fmt"func main() {a := 10b := &afmt.Printf("a: %d ptr:%p\n", a, &a) // a: 10 ptr:0xc00000a0d8fmt.Printf("b: %p type: %T\n", b, b) // b: 0xc00000a0d8 type: *int// &b 其實就是變量afmt.Println(&b) // 0xc00006a050// 解引用這個指針b,打印的就是變量a的數據fmt.Println(*b) // 10}
1.1.2、指針取值
- 在對普通變量使用
&
操作符號取地址后會獲得這個變量的指針,然后可以針對使用*
操作,也就是指針取值,代碼如下。
func main() {a := 10b := &a // 取變量a的地址, 將指針保存到b中fmt.Printf("type of b: %T\n", b)c := *b // 指針取值 (根據指針去內存取值, 此時c的類型是一個正常的int類型)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
-
總結:取地址操作符
&
和取值操作符*
是一對互補操作符,&
取出地址,*
根據地址取出地址指向的值。 -
變量、指針地址、指針變量、取地址、取值的相互關系和特性如下:
1.對變量進行取地址(&)操作,可以獲得這個變量的指針變量
2.指針變量的值是指針地址
3.對指針變量進行取值(*)操作,可以獲得指針變量指向的原變量的值
- 指針傳值示例:
func modify1(x int) {x = 100
}func modify2(x *int) {*x = 100
}func main() {a := 10modify1(a) fmt.Println(a) // 10/*把 a 的內存地址傳遞給了指針 x, x就可以在函數內部修改 a 的值了*/modify2(&a)fmt.Println(a) // 100
}
1.1.3、空指針
- 當一個指針被定義后沒有分配到任何變量時,它的值為nil
- 空指針的判斷
package mainimport "fmt"func main() {var p *stringfmt.Println(p) // 沒有賦值所以現在值為nilfmt.Printf("p的值是 %s/n", p)if p != nil {fmt.Println("非空\n")} else {fmt.Println("空值\n")}}
1.1.4、new和make
- 先來看一個例子
func main() {var a *int*a = 100fmt.Println(*a)var b map[string]intb["測試"] = 100fmt.Println(b)
}
- 執行上面的代碼會引發panic,為什么呢?在Go語言中對于引用類型的變量,我們在使用的時候不僅要聲明它,還要為它分配內存空間,是否我們的值就沒辦法存儲。而對于值類型的聲明不需要分配內存空間,是因為它們在聲明的時候已經默認分配好了內存空間。要分配內存,就引出來今天的
new
和make
。Go語言中new
和make
是內建的兩個函數,主要用來分配內存
1.1.5、new
- new是一個內置的函數,它的函數簽名如下:
func new(Type) *Type
- 其中
1.Type表示類型, new函數只接受一個參數,這個參數是一個類型
2.*Type表示類型指針, new函數返回一個指向該類型內存地址的指針
- new函數不太常用,使用new函數得到的是一個類型的指針,并且該指針對應的值為該類型的零值(可以這樣取理解,比如你的指針類型為int那么零值就是0,比如指針類型為bool那么零值就是false,這樣就在引用之指針的時候分配了內存地址,就不會報錯panic),舉個例子:
func main() {a := new(int)b := new(bool)fmt.Printf("%T\n", a) // *intfmt.Printf("%T\n", b) // *boolfmt.Println(*a) // 0fmt.Println(*b) // false
}
- 本節開始的示例代碼中
var a *int
只是聲明了一個指針變量a但是沒有初始化,指針作為引用類型需要初始化后才能會擁有內存空間,才可以給它進行賦值。應該按照如下方式使用內置的new函數對a進行初始化之后就可以正常對其賦值了:
func main() {var a *inta = new(int)fmt.Println(*a) // 0 因為int類型的默認值為 0 這就是零值
}
1.1.6、make
- make也是用于內存分配的,區別于new,它只用于slice、map以及chan的內存創建,而且它返回的類型就是這三個類型本身,而不是他們的指針類型,因為這三種類型就是引用類型,所以就沒有必要返回他們的指針了。make函數的函數簽名如下:
func make(t Type, size ..integerType) Type
- make函數是無可替代的,我們在使用slice、map以及channel的時候,都需要使用make進行初始化,然后才可以對它們進行操作。這個我們在剛剛也有提到。
- 本節開始的示例中
var b map[string]int
只是聲明變量b是一個map類型的變量,需要像下面的示例代碼一樣使用make函數進行初始化操作之后,才能對其進行鍵值對賦值:
func main() {var b map[string]intb = make(map[string]int)b["測試"] = 100fmt.Println(b)
}
1.1.7、new與make的區別
1.二者都是用來做內存分配的
2.make只用于slice、map以及channel的初始化,返回的還是這三個引用類型本身
3.、而new用于類型的內存分配,并且內存對應的值為類型零值,返回的是指向類型的指針