- 1.查看普通指针类型
- 2.指针变量类型不能转换
- 3.unsafe.Pointer类型可指针类型转换
- 3.1介绍
- 3.2任何类型的指针都可以转化成 unsafe.Pointer
- 3.3 不同类型的指针允许相互转化
- 4.内置类型unitptr
- 4.1介绍
- 4.2通过指针偏移修改结构体成员
- 5. 总结
type Admin struct {
Name string
Age int
}
func main() {
var a *int64
var b *bool
var c int32 = 64
admin1 := Admin{
Name: "seekload",
Age: 18,
}
p1 := &admin1
admin2 := &Admin{
Name: "seekload",
Age: 18,
}
p2 := &admin2
fmt.Printf("%Tn", a) //输出:*int64
fmt.Printf("%Tn", b) //输出:*bool
fmt.Printf("%Tn", &c) //输出*int32
fmt.Printf("%Tn", admin1) //输出:main.Admin
fmt.Printf("%Tn", p1) //输出:*main.Admin
fmt.Printf("%Tn", admin2) //输出:*main.Admin
fmt.Printf("%Tn", p2) //输出:**main.Admin
fmt.Println(reflect.TypeOf(p2)) //输出:**main.Admin
fmt.Println(reflect.TypeOf(Admin{Name: "seekload", Age: 18,})) //输出:main.Admin
fmt.Println(reflect.TypeOf(Admin{})) //输出:main.Admin
}
2.指针变量类型不能转换
Go 是强类型语言,声明变量之后,变量的类型是不可以改变的;
一般将 *T 看作指针类型,表示一个指向 T 类型变量的指针,不同类型的指针也不允许相互转化
(1)
(2)此类情况比较特殊
(3)
(4)
unsafe.Pointer不是函数,是一个类型;
unsafe.Pointer 通用指针类型,一种特殊类型的指针,可以包含任意类型的地址,能实现不同的指针类型之间进行转换;
官方文档里还描述了 Pointer 的四种操作规则
(1)任何类型的指针都可以转化成 unsafe.Pointer
(2)unsafe.Pointer 可以转化成任何类型的指针
(3)uintptr 可以转换为 unsafe.Pointer
(4)unsafeP.ointer 可以转换为 uintptr
var i int32 = 30 iPtr1 := &i fmt.Println(reflect.TypeOf(iPtr1)) //输出:*int32 var iPtr2 unsafe.Pointer = unsafe.Pointer(iPtr1) fmt.Println(iPtr2) //输出:0xc0000aa058 fmt.Println(reflect.TypeOf(iPtr2)) //输出:unsafe.Pointer
var i int32 = 30 iPtr1 := &i fmt.Println(reflect.TypeOf(iPtr1)) //输出:*int32 iPtr2 := unsafe.Pointer(iPtr1) fmt.Println(iPtr2) //输出:0xc000016098 fmt.Println(reflect.TypeOf(iPtr2)) //输出:unsafe.Pointer
注:不能写一下
不同类型的指针允许相互转化实际上是运用了第 1、2 条规则,我们就着例子看下:
var i int32 = 30 iPtr1 := &i fmt.Println(*iPtr1)//输出:30 *iPtr1 = 25 fmt.Println(*iPtr1, i)//输出:25 25 var iPtr2 *int64 = (*int64)(unsafe.Pointer(iPtr1)) fmt.Println(*iPtr2)//输出:25 *iPtr2 = 20 fmt.Println(*iPtr2, *iPtr1, i)//输出:20 20 20
注意:不能以下情况
uintptr 是 Go 内置类型,表示无符号整数,可存储一个完整的地址。常用于指针运算,只需将 unsafe.Pointer 类型转换成 uintptr 类型,做完加减法后,转换成 unsafe.Pointer,通过 * 操作,取值或者修改值都可以。
func main() {
var a uintptr = 4
fmt.Println(a) //输出4
}
4.2通过指针偏移修改结构体成员
func main() {
admin := Admin{
Name: "seekload",
Age: 18,
}
ptr := &admin
fmt.Println(*ptr) //输出:{seekload 18}
//这样的方式的话,获取的是第一个字段的指针
name := (*string)(unsafe.Pointer(ptr))
fmt.Println(name)
*name = "四哥"
fmt.Println(*ptr) //输出:{四哥 18}
age := (*int)(unsafe.Pointer(uintptr(unsafe.Pointer(ptr)) + unsafe.Offsetof(ptr.Age))) // 2
fmt.Println(reflect.TypeOf(age)) //输出:int
*age = 35
fmt.Println(*ptr) //输出:{四哥 35}
}
5. 总结
这篇文章我们简单介绍了普通指针类型、unsafe.Pointer 和 uintptr 之间的关系,记住三点:
(1)unsafe.Pointer 可以实现不同类型指针之间相互转化;
(2)uintptr 搭配着 unsafe.Pointer 使用,实现指针运算;
(3)不过,官方不推荐使用 unsafe 包,正如它的命名一样,是不安全的,比如涉及到内存操作,这是绕过 Go 本身设计的安全机制的,不当的操作,可能会破坏一块内存,而且这种问题非常不好定位。



