[go]指針

1、三種指針類型

  1. 普通指針 *
  2. 非類型安全指針 unsafe.Pointer(相似c的void*)
  3. 內置類型指針 uintpter(其實就是一個整數,表明地址,支持運算)
graph LR 普通指針-->unsafe.Pointer unsafe.Pointer-->普通指針 unsafe.Pointer-->uintptr uintptr-->unsafe.Pointer

普通指針和unsafe.Pointer類型的指針都能表明引用一個地址,被GC發現。可是uintptr是不表明引用一個地址的。不能被GC發現,若是沒有其餘引用可能會被回收。數組




2、普通指針 (64位系統8字節)

  • 不支持算術運算++ --
  • 不一樣類型指針不容許比較
  • 不一樣類型指針不容許相互賦值
  • 指針不容許強轉其餘類型

做用:在函數傳參時修改原值安全




3、非類型安全指針 unsafe.Pointer()

  • 能夠被轉化爲任意類型的指針

以下,將 *int 類型指針轉化爲 *float 類型函數

var p1* int
	var p2 unsafe.Pointer
	var p3* float64
	p2 = unsafe.Pointer(p1)
	p3  = (*float64)(p2)
	log.Println(reflect.TypeOf(p3))//*float64



4、uintptr內置類型

uintptr類型能夠進行指針運算,uintptr是個整數,內存地址編號ui

內存地址能夠看作是一個線性的字節數組。按字節編址,存儲單元(8位一個單元)都有一個編號,this

name :="biningo"
	namep:=unsafe.Pointer(&name)
	namept:=uintptr(namep)
	log.Printf("%x %x\n",&name,namept) //c000054200 c000054200

uintptr運算:指針

arr:=[5]int64{1,2,3,4,5}

	uptr:=uintptr(unsafe.Pointer(&arr))
	log.Printf("%x %x\n",uptr,&arr[0])

	uptr +=uintptr(8)
	log.Printf("%x %x\n",uptr,&arr[1])

	arr2:=*(*int64)(unsafe.Pointer(uptr))
	log.Println(arr2) //2
	arr2=100
	log.Println(arr[1]) //2 不變

	arr3:=(*int64)(unsafe.Pointer(uptr))
	*arr3 = 999
	log.Println(arr[1]) //999 改變

5、unsafe包的方法

下面方法返回的都是uintptr類型,uintptr類型是個整數code

  1. Alignof :查詢字節對齊的大小,大部分是8字節,有些是4字節和1字節,有多種類型混合的話就按最大的字節對齊,對象

    好比下面,字節對齊對大的是8字節 ,因此都按8字節來對齊內存

type A struct {
	n int32
	s string
	p *int
	b bool
	up uintptr
}

	a:=A{}
	log.Println(unsafe.Alignof(a.n)) //4
	log.Println(unsafe.Alignof(a.s)) //8
	log.Println(unsafe.Alignof(a.p)) //
	log.Println(unsafe.Alignof(a.b))//1
	log.Println(unsafe.Alignof(a.up))//8
type S struct {

	i1 int32 //對齊大小是 4
	i2 int32 //
}

	s:=S{}
	log.Println(unsafe.Offsetof(s.i1))//0 
	log.Println(unsafe.Offsetof(s.i2))//4  這裏就不是8開始了,由於都是4字節對齊
  1. Offsetof :返回偏移的大小
log.Println(unsafe.Offsetof(a.n)) //0
	log.Println(unsafe.Offsetof(a.s)) //8
	log.Println(unsafe.Offsetof(a.p)) //24 [+16 string的大小是16字節]
	log.Println(unsafe.Offsetof(a.b)) //32
	log.Println(unsafe.Offsetof(a.up)) //40
  1. Sizeof:返回類型的大小



6、綜合案例

操做結構體

下面展現指針堆結構體的操做,下面的操做會改變原來對象的值string

type T struct {
	t1 byte
	t2 int32
	t3 int64
	t4 string
	t5 bool
}
//普通指針,用於傳遞對象地址,不能進行指針運算。
//
//unsafe.Pointer:通用指針類型,用於轉換不一樣類型的指針,不能進行指針運算。
//
//uintptr:用於指針運算,GC 不把 uintptr 當指針,uintptr 沒法持有對象。uintptr 類型的目標會被回收。

func main() {

	t := &T{1, 2, 3, "this is a example", true}
	ptr := unsafe.Pointer(t)
	t1 := (*byte)(ptr)
	log.Println(*t1) //1

	t2 := (*int32)(unsafe.Pointer(uintptr(ptr) + unsafe.Offsetof(t.t2)))
	*t2 = 99
	log.Println(t.t2) //99 被改變了

	t3 := (*int64)(unsafe.Pointer(uintptr(ptr) + unsafe.Offsetof(t.t3)))
	*t3 = 123
	log.Println(t.t3) //123
}

操做slice

下面看看go內置的數據類型slice

type slice struct {
	array unsafe.Pointer //底層數組的指針 長度是8
	len   int //切片長度 int型size=8
	cap   int //切片實際長度
}
int main(){
    s := make([]int, 9, 20)
	var Len = *(*int)(unsafe.Pointer(uintptr(unsafe.Pointer(&s)) + uintptr(8))) 
	log.Println(Len, len(s)) // 9 9 長度是9
    var Cap = *(*int)(unsafe.Pointer(uintptr(unsafe.Pointer(&s)) + uintptr(16))) //0+8+8
	log.Println(Cap, cap(s)) // 20 20 cap是20
    
}
相關文章
相關標籤/搜索