文档章节

Go之unsafe.Pointer && uintptr类型

秋风醉了
 秋风醉了
发布于 2016/08/08 17:20
字数 1371
阅读 1.2W
收藏 2
Go

#程序员薪资揭榜#你做程序员几年了?月薪多少?发量还在么?>>>

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 main

import (
	"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 main

import (
	"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 main

import (
	"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的其它用法请参见:http://my.oschina.net/goal/blog/193698

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 main

import (
	"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=========

© 著作权归作者所有

秋风醉了
粉丝 253
博文 531
码字总数 404363
作品 0
朝阳
程序员
私信 提问
加载中

评论(0)

[转]Go里面的unsafe包详解

Golang的unsafe包是一个很特殊的包。 为什么这样说呢? 本文将详细解释。 来自go语言官方文档的警告 unsafe包的文档是这么说的: Go 1 兼容性指南这么说: 当然包名称暗示unsafe包是不安全的...

osc_8vblxxdl
2019/03/14
4
0
go语言的unsafe包(转)

The unsafe Package in Golang Golang的unsafe包是一个很特殊的包。 为什么这样说呢? 本文将详细解释。 来自go语言官方文档的警告 unsafe包的文档是这么说的: Go 1 兼容性指南这么说: 当然...

osc_e2112iac
2018/05/16
7
0
Go之unsafe.Pointer && uintptr 类型

Go语言是个强类型语言。Go语言要求所有统一表达式的不同的类型之间必须做显示的类型转换。而作为Go语言鼻祖的C语言是可以直接做隐式的类型转换的。 也就是说Go对类型要求严格,不同类型不能进...

osc_13mpeqmt
2019/04/21
14
0
【Golang】Go unsafe Pointer

Go语言在设计的时候,为了编写方便、效率高以及降低复杂度,被设计成为一门强类型的静态语言。强类型意味着一旦定义了,它的类型就不能改变了;静态意味着类型检查在运行前就做了。 同时为了...

LU_ZHAO
04/05
0
0
你不知道的Go unsafe.Pointer uintptr原理和玩法

unsafe.Pointer 这个类型比较重要,它是实现定位和读写的内存的基础,Go runtime大量使用它。官方文档对该类型有四个重要描述: 大多数指针类型会写成T,表示是“一个指向T类型变量的指针”。...

osc_6pkt76kw
2019/11/08
1
0

没有更多内容

加载失败,请刷新页面

加载更多

中点求导公式计算轨迹法向量

template<typename T> struct Point3_ { Point3_() : x(0), y(0), z(0) {} Point3_(T _x, T _y, T _z) : x(_x), y(_y), z(_z) {} T x; T y; T z;};typedef Point3_<int> ......

Ne0o0
20分钟前
24
0
怎么把手机里的图片转换成WORD?迅捷文字识别帮你解决!

怎么把手机里的图片转换成WORD?在工作和学习中,经常遇到需要记录的重要文字,例如说期末的重点,董事长的语录,这些话语常常在PPT上一闪而过,做笔记显然是来不及的,只能用手机拍摄下来。...

斯卡哈
24分钟前
14
0
自顶向下计算最右值

#include<stdio.h>int get(int n,int *sum,int flag){ int ret; if( n < 3) { if(flag == 1) { *sum += n; } ret = 1; } ......

鬼上身跳不过门槛
26分钟前
12
0
PPT怎么转PDF?用迅捷PDF转换器不用担心!

PPT怎么转PDF?在会议中我们经常会用到PPT,会议结束之后,很多公司都会要求保留会议文件,PPT自然也需要保存,但是PPT中不仅有文字、图片,还包含了很多动画效果、插入的大量数据、表格分析...

开源312
29分钟前
20
0
Vue-cli3配置全局环境变量

1.根目录下创建.env文件,里面可以用key=value的形式设置全局变量 2.全局变量的名字也就是key必须以VUE_APP_*的格式命名,也就是以VUE_APP_作为开头,例如VUE_APP_RESOURCE_URL。 3.如果要配...

如果说我们没有机会-
32分钟前
17
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部