區別于C/C++中的指標,Go語言中的指標不能進行偏移和運算,是安全指標,
要搞明白Go語言中的指標需要先知道3個概念:指標地址、指標型別和指標取值,
任何程式資料載入記憶體后,在記憶體都有他們的地址,這就是指標,而為了保存一個資料在記憶體中的地址,我們就需要指標變數,
比如,“永遠不要高估自己”這句話是我的座右銘,我想把它寫入程式中,程式一啟動這句話是要加載到記憶體(假設記憶體地址0x123456),我在程式中把這段話賦值給變數A,把記憶體地址賦值給變數B,這時候變數B就是一個指標變數,通過變數A和變數B都能找到我的座右銘,
Go語言中的指標不能進行偏移和運算,因此Go語言中的指標操作非常簡單,我們只需要記住兩個符號:&(取地址)和*(根據地址取值),
指標地址和指標型別
每個變數在運行時都擁有一個地址,這個地址代表變數在記憶體中的位置,Go語言中使用&字符放在變數前面對變數進行“取地址”操作, Go語言中的值型別(int、float、bool、string、array、struct)都有對應的指標型別,如:*int、*int64、*string等,
取變數指標的語法如下:
ptr := &v // v的型別為T
其中:
- v:代表被取地址的變數,型別為
T - ptr:用于接收地址的變數,ptr的型別就為
*T,稱做T的指標型別,*代表指標,
舉個例子:
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
}
我們來看一下b := &a的圖示:

指標取值
在對普通變數使用&運算子取地址后會獲得這個變數的指標,然后可以對指標使用*操作,也就是指標取值,代碼如下,
func main() {
//指標取值
a := 10
b := &a // 取變數a的地址,將指標保存到b中
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
總結: 取地址運算子&和取值運算子*是一對互補運算子,&取出地址,*根據地址取出地址指向的值,
變數、指標地址、指標變數、取地址、取值的相互關系和特性如下:
- 對變數進行取地址(&)操作,可以獲得這個變數的指標變數,
- 指標變數的值是指標地址,
- 對指標變數進行取值(*)操作,可以獲得指標變數指向的原變數的值,
指標傳值示例:
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
}
new和make
我們先來看一個例子:
func main() {
var a *int
*a = 100
fmt.Println(*a)
var b map[string]int
b["沙河娜扎"] = 100
fmt.Println(b)
}
執行上面的代碼會引發panic,為什么呢? 在Go語言中對于參考型別的變數,我們在使用的時候不僅要宣告它,還要為它分配記憶體空間,否則我們的值就沒辦法存盤,而對于值型別的宣告不需要分配記憶體空間,是因為它們在宣告的時候已經默認分配好了記憶體空間,要分配記憶體,就引出來今天的new和make, Go語言中new和make是內建的兩個函式,主要用來分配記憶體,
new
new是一個內置的函式,它的函式簽名如下:
func new(Type) *Type
其中,
- Type表示型別,new函式只接受一個引數,這個引數是一個型別
- *Type表示型別指標,new函式回傳一個指向該型別記憶體地址的指標,
new函式不太常用,使用new函式得到的是一個型別的指標,并且該指標對應的值為該型別的零值,舉個例子:
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)
}
make
make也是用于記憶體分配的,區別于new,它只用于slice、map以及chan的記憶體創建,而且它回傳的型別就是這三個型別本身,而不是他們的指標型別,因為這三種型別就是參考型別,所以就沒有必要回傳他們的指標了,make函式的函式簽名如下:
func make(t Type, size ...IntegerType) Type
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的區別
- 二者都是用來做記憶體分配的,
- make只用于slice、map以及channel的初始化,回傳的還是這三個參考型別本身;
- 而new用于型別的記憶體分配,并且記憶體對應的值為型別零值,回傳的是指向型別的指標,
轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/43086.html
標籤:Go
上一篇:Go語言基礎之結構體
下一篇:Go語言基礎之函式
