1. 程式人生 > 實用技巧 >Go語言基礎之指標

Go語言基礎之指標

Go語言的指標不能進行偏移和計算,是安全的指標。

搞明白指標的必備三個條件:

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

Go語言中的指標

一個變數在程式中都有他們的記憶體地址,這就是指標。為了儲存資料在記憶體中的地址,那麼我們就需要指標變數。

Go語言中的指標是安全的,因此我們只需要記住兩個符號& 取地址* 根據地址取值

指標型別和指標地址

Go語言中的值型別(int, float, bool, string, array, struct)都有對應的指標型別,如:*int, *int64, *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)  // a: 10 ptr: 0xc0000140b0
	fmt.Printf("b:%p type: %T\n", b, b)  // b:0xc0000140b0 type: *int
	fmt.Println(&b)  // 0xc000006028  指標也需要一個地址去儲存
}

指標取值

在對普通變數使用&操作符取地址後會獲得這個變數的指標,然後可以對指標使用*操作,也就是指標取值,程式碼如下

func main() {
    a := 10
    b := &a
    fmt.Printf("type of b: %T\n", b)  // 去變數a的地址,將指標儲存到b中
    c := *b  // 指標取值  根據指標去記憶體取值
}

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

總結:取地址操作符&和取值操作符*是一對互補操作符,&取出地址,*根據地址取出地址指向的值。

變數、指標地址、指標變數、取地址、取值的相互關係和特性如下:

  • 對變數進行取地址(&)操作,可以獲得這個變數的指標變數。
  • 指標變數的值是指標地址。
  • 對指標變數進行取值(*)操作,可以獲得指標變數指向的原變數的值。

指標傳值例項

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)

	modify2(&a)
	fmt.Println(a)
}

new 和 make

請你們破個案

func main() {
	var a *int
	*a = 100
	fmt.Println(*a)
}  // 這個程式會打印出來啥?

// 答案是報錯了,panic: runtime error: invalid memory address or nil pointer dereference
  • 為啥呢?
  • 在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)
	c := new(string)
	fmt.Printf("%T\n", a)  // *int
	fmt.Printf("%T\n", b)  // *bool
	fmt.Printf("%T\n", c)  // *string
	fmt.Println(*a)  // 0
	fmt.Println(*b)  // 此處為空字串
	fmt.Println(*c)  // false
}

make

make也是用於記憶體分配的,區別於new,它只用於slicemap以及chan的記憶體建立而且它返回的型別就是這三個型別本身,而不是他們的指標型別,因為這三種類型就是引用型別,所以就沒有必要返回他們的指標了。make函式的函式簽名如下:

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

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

func main() {
	a := map[string]int{}
	fmt.Printf("%T\n", a)  //map[string]int  返回的就是本身,因為是引用型別
	a["hello"] = 10
	fmt.Println(a)
	
	var b map[string]int
	b = make(map[string]int, 10)
	b["hello"] = 40
	fmt.Println(b)
}

newmake的區別

  1. 二者都是用來做記憶體分配的
  2. make只用於slicemap以及chan型別的初始化,返回的還是這三個型別的本身
  3. new用於型別的記憶體分配,並且記憶體對應的值為型別零值,返回的是指向型別的指標