博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
Go之unsafe.Pointer && uintptr类型
阅读量:5875 次
发布时间:2019-06-19

本文共 3343 字,大约阅读时间需要 11 分钟。

hot3.png

Go之unsafe.Pointer && uintptr类型

unsafe.Pointer

这个类型比较重要,它是实现定位欲读写的内存的基础。官方文档对该类型有四个重要描述:

(1)任何类型的指针都可以被转化为Pointer(2)Pointer可以被转化为任何类型的指针(3)uintptr可以被转化为Pointer(4)Pointer可以被转化为uintptr

大多数指针类型会写成T,表示是“一个指向T类型变量的指针”。unsafe.Pointer是特别定义的一种指针类型(译注:类似C语言中的void类型的指针),它可以包含任意类型变量的地址。当然,我们不可以直接通过*p来获取unsafe.Pointer指针指向的真实变量的值,因为我们并不知道变量的具体类型。和普通指针一样,unsafe.Pointer指针也是可以比较的,并且支持和nil常量比较判断是否为空指针。

一个普通的T类型指针可以被转化为unsafe.Pointer类型指针,并且一个unsafe.Pointer类型指针也可以被转回普通的指针,被转回普通的指针类型并不需要和原始的T类型相同。

通过将float64类型指针转化为uint64类型指针,我们可以查看一个浮点数变量的位模式。

package mainimport (	"fmt"	"unsafe"	"reflect")func Float64bits(f float64) uint64 {	fmt.Println(reflect.TypeOf(unsafe.Pointer(&f)))  //unsafe.Pointer	fmt.Println(reflect.TypeOf((*uint64)(unsafe.Pointer(&f))))  //*uint64	return *(*uint64)(unsafe.Pointer(&f))}func main() {	fmt.Printf("%#016x\n", Float64bits(1.0)) // "0x3ff0000000000000"}

再看一个例子,

package mainimport (	"fmt"	"reflect")func main() {	v1 := uint(12)	v2 := int(12)	fmt.Println(reflect.TypeOf(v1)) //uint	fmt.Println(reflect.TypeOf(v2)) //int	fmt.Println(reflect.TypeOf(&v1)) //*uint	fmt.Println(reflect.TypeOf(&v2)) //*int	p := &v1	//两个变量的类型不同,不能赋值	//p = &v2 //cannot use &v2 (type *int) as type *uint in assignment	fmt.Println(reflect.TypeOf(p)) // *unit}
当再次把 v2 的指针赋值给p时,会发生错误cannot use &v2 (type *int) as type *uint in assignment,也就是说类型不同,一个是*int,一个是*uint。

这时,可以使用unsafe.Pointer进行转换,如下,

package mainimport (	"fmt"	"reflect"	"unsafe")func main() {	v1 := uint(12)	v2 := int(13)	fmt.Println(reflect.TypeOf(v1)) //uint	fmt.Println(reflect.TypeOf(v2)) //int	fmt.Println(reflect.TypeOf(&v1)) //*uint	fmt.Println(reflect.TypeOf(&v2)) //*int	p := &v1	p = (*uint)(unsafe.Pointer(&v2)) //使用unsafe.Pointer进行类型的转换	fmt.Println(reflect.TypeOf(p)) // *unit	fmt.Println(*p)                //13}

关于unsafe.Pointer的其它用法请参见:

uintptr

// uintptr is an integer type that is large enough to hold the bit pattern of// any pointer.type uintptr uintptr

uintptr是golang的内置类型,是能存储指针的整型,在64位平台上底层的数据类型是,

typedef unsigned long long int  uint64;typedef uint64          uintptr;

一个unsafe.Pointer指针也可以被转化为uintptr类型,然后保存到指针型数值变量中(注:这只是和当前指针相同的一个数字值,并不是一个指针),然后用以做必要的指针数值运算。(uintptr是一个无符号的整型数,足以保存一个地址)这种转换虽然也是可逆的,但是将uintptr转为unsafe.Pointer指针可能会破坏类型系统,因为并不是所有的数字都是有效的内存地址。

许多将unsafe.Pointer指针转为原生数字,然后再转回为unsafe.Pointer类型指针的操作也是不安全的。比如下面的例子需要将变量x的地址加上b字段地址偏移量转化为*int16类型指针,然后通过该指针更新x.b:

package mainimport (	"fmt"	"unsafe")func main() {	var x struct {		a bool		b int16		c []int	}	/**	unsafe.Offsetof 函数的参数必须是一个字段 x.f, 然后返回 f 字段相对于 x 起始地址的偏移量, 包括可能的空洞.	*/	/**	uintptr(unsafe.Pointer(&x)) + unsafe.Offsetof(x.b)	指针的运算	*/	// 和 pb := &x.b 等价	pb := (*int16)(unsafe.Pointer(uintptr(unsafe.Pointer(&x)) + unsafe.Offsetof(x.b)))	*pb = 42	fmt.Println(x.b) // "42"}

上面的写法尽管很繁琐,但在这里并不是一件坏事,因为这些功能应该很谨慎地使用。不要试图引入一个uintptr类型的临时变量,因为它可能会破坏代码的安全性(注:这是真正可以体会unsafe包为何不安全的例子)。

下面段代码是错误的:

// NOTE: subtly incorrect!tmp := uintptr(unsafe.Pointer(&x)) + unsafe.Offsetof(x.b)pb := (*int16)(unsafe.Pointer(tmp))*pb = 42

产生错误的原因很微妙。有时候垃圾回收器会移动一些变量以降低内存碎片等问题。这类垃圾回收器被称为移动GC。当一个变量被移动,所有的保存改变量旧地址的指针必须同时被更新为变量移动后的新地址。从垃圾收集器的视角来看,一个unsafe.Pointer是一个指向变量的指针,因此当变量被移动是对应的指针也必须被更新;但是uintptr类型的临时变量只是一个普通的数字,所以其值不应该被改变。上面错误的代码因为引入一个非指针的临时变量tmp,导致垃圾收集器无法正确识别这个是一个指向变量x的指针。当第二个语句执行时,变量x可能已经被转移,这时候临时变量tmp也就不再是现在的&x.b地址。第三个向之前无效地址空间的赋值语句将彻底摧毁整个程序!

=========END=========

转载于:https://my.oschina.net/xinxingegeya/blog/729673

你可能感兴趣的文章
前端工程化系列[01]-Bower包管理工具的使用
查看>>
使用 maven 自动将源码打包并发布
查看>>
Spark:求出分组内的TopN
查看>>
Python爬取豆瓣《复仇者联盟3》评论并生成乖萌的格鲁特
查看>>
关于跨DB增量(增、改)同步两张表的数据小技巧
查看>>
飞秋无法显示局域网好友
查看>>
学员会诊之03:你那惨不忍睹的三层架构
查看>>
vue-04-组件
查看>>
Golang协程与通道整理
查看>>
解决win7远程桌面连接时发生身份验证错误的方法
查看>>
C/C++ 多线程机制
查看>>
js - object.assign 以及浅、深拷贝
查看>>
python mysql Connect Pool mysql连接池 (201
查看>>
Boost在vs2010下的配置
查看>>
android camera(四):camera 驱动 GT2005
查看>>
一起谈.NET技术,ASP.NET伪静态的实现及伪静态的意义
查看>>
20款绝佳的HTML5应用程序示例
查看>>
string::c_str()、string::c_data()及string与char *的正确转换
查看>>
11G数据的hive初测试
查看>>
如何使用Core Text计算一段文本绘制在屏幕上之后的高度
查看>>