golang
是一种强类型的静态语言,不支持不同类型之间的隐式转换,比如int
转int64
这种,也不支持不同指针类型之间的转换,比如*int
转换为*int64
;当然也不支持指针运算的操作了
但是,在go
中却提供了unsafe.Pointer
和uintptr
这两种类型,来间接的完成不同指针类型的转化和指针的运算操作
unsafe.Pointer
是什么
unsafe.Pointer
是一个特殊的类型,可以持有任何类型的指针,就像是 C
中的void *
一样,比如:
func main(){
k := 123
p := unsafe.Pointer(&k) //此时 p 就是一个指向 k 的指针
fmt.Println(p)
}
## Output
0xc00004c1f0 // 输出的就是存储 k 的地址
但是,unsafe.Pointer
表示的指针不能进行指针运算,也不能用*p
的方式输出它指向的那个地址中的值;它就只是一个桥梁,用来将其转换为其他的指针类型,比如:
func main() {
var k int64 = 123
p := unsafe.Pointer(&k)
var pp *int
pp = (*int)(p) //将其转化为了 *int 类型,然后使用, *pp 的方式输出它的值
fmt.Println(pp)
fmt.Println(*pp)
}
## output
0xc0000ba058
123
uintptr
是什么
uintptr
就是一个无符号的整型,它足够大能够保存任何类型的指针;它跟unsafe.Pointer
不同,uintptr
保存的是一个指针的十进制,比如:
func main() {
k := 123
h := unsafe.Pointer(&k)
fmt.Println(h)
o := uintptr(h)
fmt.Println(o)
fmt.Printf("%x\n",o) //输出它的十六进制
}
### Output
0xc00000a0b0
824633761968
c00000a0b0
将其转化为十六进制就会发现其实它保存的它的十六进制值和unsafe.Pointer
输出的是一样的;uintptr
是可以进行指针运算的,比如:
func main() {
k := 123
h := unsafe.Pointer(&k)
fmt.Println(h)
o := uintptr(h)
fmt.Println(o)
fmt.Printf("%x\n",o)
o = o + unsafe.Sizeof(k)
fmt.Println(o)
fmt.Printf("%x\n",o)
}
## Output
0xc00000a0b0
824633761968
c00000a0b0
824633761976
c00000a0b8
可以发现它的运算结果就是在原来的地址加了一个整型类型的大小;uintptr
是不能直接转化为某个具体类型的,比如:
func main() {
k := 123
p := uintptr(k)
var pp *int32
pp = (*int32) (p)
}
这种就会编译失败,所以首先需要将其转化为unsafe.Pointer
,然后再把它转化为具体的指针类型,比如:
func main() {
k := 123
p := uintptr(k)
fmt.Println(p)
fmt.Println(&p)
u := unsafe.Pointer(&p)
var pp *int32
pp = (*int32) (u)
fmt.Println(pp)
fmt.Println(*pp)
}
## Output
123
0xc000126058
0xc000126058
123
这样就可以实现正常的转化了
总结
golang
提供的这种操作指针的方式是很容易引起内存泄露的,所以没必要还是不要使用这种方式操作指针为好