前言
在Go里面pointer就是1種可以把內存地址的存儲起來的數據類型。我們使用pointer數據類型的變量可以記錄下另1個變量的內存地址,方便我們修改這變量的值。
為什么Go中使用了指針?
因為指針可以幫助我們節省內存,我們知道在程序運行時值類型的變量被賦值之后會對值進行重新拷貝,如果我們每次拷貝的是1個指針類型的變量呢?
還有Go函數里面傳遞的參數都是副本也就是重新copy一份,我們如何在函數中修該1個外部變量。我們可以通過記錄下值類型變量的內存地址,來達到修改值類型變量的目的。
區別於C/C++中的指針,Go語言中的指針不能進行偏移和運算,只能讀取指針的位置,是安全指針,所有Go里面的指針比較簡單。
還有很重要的一點就是我們可以對1個真正存儲值的變量,設置多個指針,就可以做引用數據類型。起到節省內存的效果。
只需要記住以下幾點:
&變量名: 獲取變量的內存地址
*pointor:通過指針類型的變量,獲取該指針指向的值
v1:="張根" p1:=&v1 fmt.Println(v1,p1,*p1) //張根 0xc0001041e0 張根
什么是指針
不管是Python還是Go程序 執行過程中數據(變量)載入內存后,在計算機內存中都有該變量所在的內存地址,這就是指針。
var v3 *int v4:=new(int)
指針就是1個保存了另1個變量所在內存地址的變量,該變量記錄了X\y\a\s變量的內存地址,我們就可以通過指針變量在廣袤的內存上快速查找到X\y\a\s變量。對其就行修改。
package main import "fmt" func main(){ var a int a=100 b:=&a //變量a的數據類型:int類型,變量b的數據類型*int的指針 fmt.Printf("變量a的數據類型:%T,變量b的數據類型%T\n",a,b ) //把變量a的內存地址打印出來 fmt.Printf("變量a的內存地址:%p\n",&a)//&a獲取變量的內存地址也就是指針啦! //把變量b的值打印出來 fmt.Printf("變量b的值也就是變量a的內存地址為:%p\n",b) fmt.Printf("變量b的值也就是變量b的值,也就是變量a的內存地址:%v\n",b) //把變量b的內存地址打印出來:也就是存儲變量a的內存地址的內存地址 fmt.Printf("變量b的內存地址為:%p\n",&b) }
在Go語言中的值類型(int、float、bool、string、array、struct)都有對應的指針類型,如:*int
、*int64
、*string
等。
指針操作
package main import "fmt" func main() { //基本數據類型在內存中的布局 var i int = 10 //獲取變量i的內存地址 fmt.Println("變量i的內存地址:", &i) //聲明1個 ptr指針變量,指向1個類型為int的內存地址 var ptr *int = &i fmt.Println(ptr) //獲取指針的內存地址 fmt.Println(&ptr) //通過指針獲取變量i對應的值(10) fmt.Println(*ptr) //通過指針修改變量i對應的值 *ptr = 222 fmt.Println(i) //通過指針修改變量的值 name := "成龍" age := "18" por1 := &name *por1 = "JackCheng" por1 = &age *por1 = "68" fmt.Println(name, age) }
golang的指針是安全的默認不能進行偏移和運算,只能讀取指針的位置。但是如果它變成unsafe的也是可以的。
package main import ( "fmt" "reflect" "unsafe" ) func main() { //數組是一塊連續的內存,第1個元素的內存地址代指整個數組 userList := [3]int8{11, 22, 33} //但是數組的第1個元素和數組的指針類型不同 //*[3]int8 //*int8 fmt.Println(reflect.TypeOf(&userList)) fmt.Println(reflect.TypeOf(&userList[0])) //0xc000062330 0xc000062340 0xc000062350 fmt.Println(&userList[0], &userList[1], &userList[2]) //1.獲取數組第1個元素的內存地址 var firstItemPtr *int8 = &userList[0] //2.轉換成Pointer類型 ptr := unsafe.Pointer(firstItemPtr) //3.轉換成uintptr類型,然后進行內存地址計算(即地址增加1個字節,意味着2個索引位置) targetAdress := uintptr(ptr) + 1 fmt.Println(targetAdress) //4.根據新地址重新轉換成Pointer類型 newPtr := unsafe.Pointer(targetAdress) //5.Pointer對象轉換成 int8指針類型 value := (*int8)(newPtr) fmt.Println(*value) }
new和make的區別
前面我知道使用make可以創建1個切片數據類型的變量,並且該slice在沒有賦值前,就有默認值(開辟了內存空間);
make 和new在Go中都是用於申請內存的
new用於給Go中基本的數據類型申請內存(int/string/bool)返回的是對應數據類型的指針(*int/*string/*bool)
make用於給復雜數據類型申請內存(slice/map/chanel/struct),返回值=默認值的數據類型本身。
package main import "fmt" func main(){ var a1 *int //只是聲明int類型的變量,不開辟內存地址 fmt.Println(a1)//nil var a2=new(int) //申請1個 int指針的變量,開辟內存地址 fmt.Println(a2) *a2=100 fmt.Println(*a2) }