Go語言基礎之指針

區別於C/C++中的指針,Go語言中的指針不能進行偏移和運算,是安全指針。安全

要搞明白Go語言中的指針須要先知道3個概念:指針地址、指針類型和指針取值。ide

任何程序數據載入內存後,在內存都有他們的地址,這就是指針。而爲了保存一個數據在內存中的地址,咱們就須要指針變量。函數

好比,「永遠不要高估本身」這句話是個人座右銘,我想把它寫入程序中,程序一啓動這句話是要加載到內存(假設內存地址0x123456),我在程序中把這段話賦值給變量A,把內存地址賦值給變量B。這時候變量B就是一個指針變量。經過變量A和變量B都能找到個人座右銘。post

Go語言中的指針不能進行偏移和運算,所以Go語言中的指針操做很是簡單,咱們只須要記住兩個符號:&(取地址)和*(根據地址取值)。spa

指針地址和指針類型

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

取變量指針的語法以下:code

ptr := &v    // v的類型爲T

其中:視頻

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

舉個例子:blog

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的區別

  1. 兩者都是用來作內存分配的。
  2. make只用於slice、map以及channel的初始化,返回的仍是這三個引用類型自己;
  3. 而new用於類型的內存分配,而且內存對應的值爲類型零值,返回的是指向類型的指針。

轉載自李文周博客

相關文章
相關標籤/搜索