unsafe内容介绍

type ArbitraryType int
type Pointer *ArbitraryType
func Sizeof(x ArbitraryType) uintptr
func Offsetof(x ArbitraryType) uintptr
func Alignof(x ArbitraryType) uintptr

unsafe包只有两个类型,三个函数,但是功能很强大。

unsafe 库让 golang 可以像C语言一样操作计算机内存,但这并不是golang推荐使用的,能不用尽量不用,就像它的名字所表达的一样,它绕过了golang的内存安全原则,是不安全的,容易使你的程序出现莫名其妙的问题,不利于程序的扩展与维护。

先简单介绍下Golang指针

  1. *类型:普通指针,用于传递对象地址,不能进行指针运算。

  2. unsafe.Pointer:通用指针类型,用于转换不同类型的指针,不能进行指针运算。

  3. uintptr:用于指针运算,GC 不把 uintptr 当指针,uintptr 无法持有对象。uintptr 类型的目标会被回收

  unsafe.Pointer 可以和 普通指针 进行相互转换。

  unsafe.Pointer 可以和 uintptr 进行相互转换。

也就是说 unsafe.Pointer 是桥梁,可以让任意类型的指针实现相互转换,也可以将任意类型的指针转换为 uintptr 进行指针运算。

uintptr这个类型,在golang中,字节长度也是与int一致。通常Pointer不能参与运算,比如你要在某个指针地址上加上一个偏移量,Pointer是不能做这个运算的,那么谁可以呢?就是uintptr类型了,只要将Pointer类型转换成uintptr类型,做完加减法后,转换成Pointer,通过*操作,取值,修改值,随意。

两个类型简介

// ArbitraryType is here for the purposes of documentation only and is not actually
// part of the unsafe package. It represents the type of an arbitrary Go expression.
type ArbitraryType int type Pointer *ArbitraryType

ArbitraryType是int的一个别名,在Go中对ArbitraryType赋予特殊的意义。代表一个任意Go表达式类型。

Pointer 是int指针类型的一个别名,在Go中可以把Pointer类型,理解成任何指针的父类型。

下面为官方文档中对Pointer的使用场景介绍

Pointer represents a pointer to an arbitrary type. There are four special operationsavailable for type Pointer that are not available for other types:
- A pointer value of any type can be converted to a Pointer.
- A Pointer can be converted to a pointer value of any type.
- A uintptr can be converted to a Pointer.
- A Pointer can be converted to a uintptr. Pointer therefore allows a program to defeat the type system and read and write arbitrary memory. It should be used with extreme care.

golang的指针类型长度与int类型长度,在内存中占用的字节数是一样的.

ArbitraryType类型的变量也可以是指针。所以,千万不要死磕type后边的那个int

三个函数简介

// Sizeof takes an expression x of any type and returns the size in bytes
// of a hypothetical variable v as if v was declared via var v = x.
// The size does not include any memory possibly referenced by x.
// For instance, if x is a slice, Sizeof returns the size of the slice
// descriptor, not the size of the memory referenced by the slice.
**func Sizeof(x ArbitraryType) uintptr** // Offsetof returns the offset within the struct of the field represented by x,
// which must be of the form structValue.field. In other words, it returns the
// number of bytes between the start of the struct and the start of the field.
**func Offsetof(x ArbitraryType) uintptr** // Alignof takes an expression x of any type and returns the required alignment
// of a hypothetical variable v as if v was declared via var v = x.
// It is the largest value m such that the address of v is always zero mod m.
// It is the same as the value returned by reflect.TypeOf(x).Align().
// As a special case, if a variable s is of struct type and f is a field
// within that struct, then Alignof(s.f) will return the required alignment
// of a field of that type within a struct. This case is the same as the
// value returned by reflect.TypeOf(s.f).FieldAlign().
**func Alignof(x ArbitraryType) uintptr**

通过分析发现,这三个函数的参数均是ArbitraryType类型,就是接受任何类型的变量。

  1. Alignof返回变量对齐字节数量
  2. Offsetof返回变量指定属性的偏移量,这个函数虽然接收的是任何类型的变量,但是有一个前提,就是变量要是一个struct类型,且还不能直接将这个struct类型的变量当作参数,只能将这个struct类型变量的属性当作参数。
  3. Sizeof 返回变量在内存中占用的字节数,切记,如果是slice,则不会返回这个slice在内存中的实际占用长度

示例

Sizeof

unsafe.Sizeof函数返回的就是uintptr类型的值(表达式,即值的大小):

var p float64 = 99
fmt.Println(reflect.TypeOf(unsafe.Sizeof(p)))
fmt.Println(unsafe.Sizeof(p))

results:

uintptr
8

unsafe.Sizeof接受任意类型的值(表达式),返回其占用的字节数,在上面的例子中float64的大小是8bytes。

如果传入一个指针类型的对象会返回多少呢?

type W struct {
a byte
b int32
c int64
} var w *W
fmt.Println(unsafe.Sizeof(w)) //4 or 8

一般情况下,可能是4或8,因为w是指针类型uintptr,而uintptr是平台相关的,在32位系统下大小是4bytes,在64位系统下是8bytes。

要获取值类型的大小,需要对指针变量进行取值:

fmt.Println(unsafe.Sizeof(*w)) //16

对齐

在上面的例子中,*w的大小为16,按照常理来说,byte占用1字节,int32占用4字节,int64占用8字节,大小应该是13才对。这是因为发生了对齐,unsafe.Alignof可以计算对齐值:

unsafe.Alignof(w.a)   // type byte
unsafe.Alignof(w.b) // type int32
unsafe.Alignof(w.c) // type int64

分别是1、4、8,因为int32类型的对齐值是4bytes,必须是4的倍数,故byte类型要填充3个字节。而填充后,两者的大小和为8bytes,int64对齐值是8bytes,不需要填充,所以用unsafe.Sizeof获取到结构的大小为4+4+8=16。

反射包的对齐方法

反射包也有某些方法可用于计算对齐值:

unsafe.Alignof(w)等价于reflect.TypeOf(w).Align。

unsafe.Alignof(w.i)等价于reflect.Typeof(w.i).FieldAlign()。

结构体的对齐值

如果我们计算的是结构体的对齐值而不是某个字段或者基本类型,那么值会是多少呢?

type W struct {
a byte
b int32
c int64
} var w *W
var w2 W fmt.Println(unsafe.Alignof(w))
fmt.Println(unsafe.Alignof(w2))
fmt.Println(reflect.TypeOf(w).Elem().Align())

results:

8
8
8

64位机器下,指针对象的对齐值是8,因为指针类型是uintptr。而结构体的值类型却是8bytes的对齐值,这是因为会先进行字段的对齐,字段最大的对齐值是8bytes,因此结构体值类型的对齐值也是8。

更改结构,验证一下:

type W struct {
a byte
b int32
c int32
}
var w W
fmt.Println(unsafe.Alignof(w)) //4

综合示例

type T struct {
t1 byte
t2 int32
t3 int64
t4 string
t5 bool
} fmt.Println("----------unsafe.Pointer---------")
t := &T{1, 2, 3, "this is a example", true}
ptr := unsafe.Pointer(t)
t1 := (*byte)(ptr)
fmt.Println(*t1)
t2 := (*int32)(unsafe.Pointer(uintptr(ptr) + unsafe.Offsetof(t.t2)))
*t2 = 99
fmt.Println(t)
t3 := (*int64)(unsafe.Pointer(uintptr(ptr) + unsafe.Offsetof(t.t3)))
fmt.Println(*t3)
*t3 = 123
fmt.Println(t)

results:

----------unsafe.Pointer---------
1
&{1 99 3 this is a example true}
3
&{1 99 123 this is a example true}

借助于 unsafe.Pointer,我们实现了像 C 语言中的指针偏移操作。可以看出,这种不安全的操作使得我们可以在任何地方直接访问结构体中未公开的成员,只要能得到这个结构体变量的地址。

参考资料:

  1. https://blog.csdn.net/libing_thinking/article/details/49907815
  2. https://studygolang.com/articles/6903
  3. https://blog.csdn.net/hzwy23/article/details/60893765
  4. https://www.cnblogs.com/golove/p/5909968.html
  5. https://studygolang.com/articles/1414

Go语言之unsafe包介绍及使用的更多相关文章

  1. go语言的unsafe包(转)

    The unsafe Package in Golang Golang的unsafe包是一个很特殊的包. 为什么这样说呢? 本文将详细解释. 来自go语言官方文档的警告 unsafe包的文档是这么说的 ...

  2. unsafe包的学习和使用

    Go语言之unsafe包介绍及使用 unsafe内容介绍 type ArbitraryType int type Pointer *ArbitraryType func Sizeof(x Arbitr ...

  3. 深度解密Go语言之unsafe

    目录 指针类型 什么是 unsafe 为什么有 unsafe unsafe 实现原理 unsafe 如何使用 获取 slice 长度 获取 map 长度 map 源码中的应用 Offsetof 获取成 ...

  4. R语言︱H2o深度学习的一些R语言实践——H2o包

    每每以为攀得众山小,可.每每又切实来到起点,大牛们,缓缓脚步来俺笔记葩分享一下吧,please~ --------------------------- R语言H2o包的几个应用案例 笔者寄语:受启发 ...

  5. [转]Go里面的unsafe包详解

    Golang的unsafe包是一个很特殊的包. 为什么这样说呢? 本文将详细解释. 来自go语言官方文档的警告 unsafe包的文档是这么说的: 导入unsafe的软件包可能不可移植,并且不受Go 1 ...

  6. Go语言基础之包

    Go语言基础之包 在工程化的Go语言开发项目中,Go语言的源码复用是建立在包(package)基础之上的.本文介绍了Go语言中如何定义包.如何导出包的内容及如何导入其他包. Go语言的包(packag ...

  7. 使用R语言的RTCGA包获取TCGA数据--转载

    转载生信技能树 https://mp.weixin.qq.com/s/JB_329LCWqo5dY6MLawfEA TCGA数据源 - R包RTCGA的简单介绍 - 首先安装及加载包 - 指定任意基因 ...

  8. Spring4相关jar包介绍(转)

    Spring4相关jar包介绍 spring-core.jar(必须):这个jar 文件包含Spring 框架基本的核心工具类.Spring 其它组件要都要使用到这个包里的类,是其它组件的基本核心,当 ...

  9. R语言中常用包(二)

    数据导入 以下R包主要用于数据导入和保存数据 feather:一种快速,轻量级的文件格式.在R和python上都可使用readr:实现表格数据的快速导入.中文介绍可参考这里readxl:读取Micro ...

随机推荐

  1. at android.view.Surface.unlockCanvasAndPost(Native Method)

    at android.view.Surface.unlockCanvasAndPost(Native Method) 在绘制动画特效的时候点击back键会报以上异常. 主要原因:当点击back按钮时A ...

  2. Hive 数据的导入导出

    数据的导入: 通过文件导入,使用load命令 一.导入本地文件: load data local inpath '/home/hadoop/files/emp.txt' overwrite into ...

  3. BZOJ1767/Gym207383I CEOI2009 Harbingers 斜率优化、可持久化单调栈、二分

    传送门--BZOJCH 传送门--VJ 注:本题在BZOJ上是权限题,在Gym里面也不能直接看,所以只能在VJ上交了-- 不难考虑到这是一个\(dp\). 设\(dep_x\)表示\(x\)在树上的带 ...

  4. (转)Ubuntu无法找到add-apt-repository问题的解决方法

    原文 网上查了一下资料,原来是需要 python-software-properties 于是 apt-get install python-software-properties 除此之外还要安装 ...

  5. [Oracle]Oracle Fail Safe 与 SQLNET.AUTHENTICATION_SERVICES关系

    现象: 在使用 OFS (Oracle Fail Safe)的环境中,把数据库的 SQLNET.AUTHENTICATION_SERVICES 从 NTS 改为 NONE之后,当从 Oracle Fa ...

  6. 【强化学习】python 实现 q-learning 例三(例一改写)

    本文作者:hhh5460 本文地址:https://www.cnblogs.com/hhh5460/p/10139738.html 例一的代码是函数式编写的,这里用面向对象的方式重新撸了一遍.好处是, ...

  7. 浏览器跨域请求之credentials

    -时间起源- 前段时间,需要弄个简单的网站出来,访问远程的api服务. 我是这么做的.首先是在搭建一个nodejs服务来运行前端页面.在我请求登录的时候,能成功返回相应的成功信息.然后,当我再次请求读 ...

  8. Linux内核分析作业第八周

    进程的切换和系统的一般执行过程 一.进程调度的时机 中断处理过程(包括时钟中断.I/O中断.系统调用和异常)中,直接调用schedule(),或者返回用户态时根据need_resched标记调用sch ...

  9. StringBuffer的append方法比“+”高效

    在字符串的连接过程中StringBuffer的效率要比String高: string操作代码: String str = new String("welcome to "); st ...

  10. 剑指offer:树的子结构

    题目描述: 输入两棵二叉树A,B,判断B是不是A的子结构.(ps:我们约定空树不是任意一个树的子结构) 解题思路: 同样考虑用递归来做. 利用两个递归函数,一个用于判断两棵树树否相等,另一个递归取A的 ...