[go]指標
阿新 • • 發佈:2020-04-12
一、三種指標型別
- 普通指標 *
- 非型別安全指標 unsafe.Pointer(類似c的void*)
- 內建型別指標 uintpter(其實就是一個整數,代表地址,支援運算)
普通指標和unsafe.Pointer型別的指標都能代表引用一個地址,被GC發現。但是uintptr是不代表引用一個地址的。不能被GC發現,如果沒有其他引用可能會被回收。
二、普通指標 (64位系統8位元組)
- 不支援算術運算++ --
- 不同型別指標不允許比較
- 不同型別指標不允許相互賦值
- 指標不允許強轉其他型別
作用:在函式傳參時修改原值
三、非型別安全指標 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
四、uintptr內建型別
uintptr型別可以進行指標運算,uintptr是個整數,記憶體地址編號
記憶體地址可以看做是一個線性的位元組陣列。按位元組編址,儲存單元(8位一個單元)都有一個編號,
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 改變
五、unsafe包的方法
下面方法返回的都是uintptr型別,uintptr型別是個整數
-
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位元組對齊
- 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
- Sizeof:返回型別的大小
六、綜合案例
操作結構體
下面展示指標堆結構體的操作,下面的操作會改變原來物件的值
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
}