Go语言之unsafe包介绍及使用

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 语言中的指针偏移操作。可以看出,这种不安全的操作使得我们可以在任何地方直接访问结构体中未公开的成员,只要能得到这个结构体变量的地址。

unsafe包的学习和使用的更多相关文章

  1. java concurrent包的学习(转)

    java concurrent包的学习(转) http://my.oschina.net/adwangxiao/blog/110188 我们都知道,在JDK1.5之前,Java中要进行业务并发时,通常 ...

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

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

  3. Go语言之unsafe包介绍及使用

    unsafe内容介绍 type ArbitraryType int type Pointer *ArbitraryType func Sizeof(x ArbitraryType) uintptr f ...

  4. go语言path包和filepath包的学习与使用

    path包的使用 package main; import ( "fmt" "path" ) //go语言path包的学习 func main() { //返回 ...

  5. Java中的sun.misc.Unsafe包

    chronicle项目:https://github.com/peter-lawrey/Java-Chronicle 这个项目是利用mmap机制来实现高效的读写数据,号称每秒写入5到20百万条数据. ...

  6. go语言的unsafe包(转)

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

  7. Wireshark 抓包过滤器学习

    Wireshark 抓包过滤器学习 wireshark中,分为两种过滤器:捕获过滤器 和 显示过滤器 捕获过滤器 是指wireshark一开始在抓包时,就确定要抓取哪些类型的包:对于不需要的,不进行抓 ...

  8. 常用统计分析python包开源学习代码 numpy pandas matplotlib

    常用统计分析python包开源学习代码 numpy pandas matplotlib 待办 https://github.com/zmzhouXJTU/Python-Data-Analysis

  9. Golang学习 - unsafe 包

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

随机推荐

  1. python 2.7 环境配置

    原文地址:Python 2.7的安装(64位win10) Python 2.7.12 下载地址:https://www.python.org/downloads/ 安装路径D:\Program Fil ...

  2. Struts笔记2

    Struts2-配置文件result元素 作用:为动作指定结果视图 name属性:逻辑视图的名称,对应着动作方法的返回值.默认值是success type属性:结果类型,指的就是用什么方式转到定义的页 ...

  3. 作业练习P194,jieba应用,读取,分词,存储,生成词云,排序,保存

    import jieba #第一题 txt='Python是最有意思的编程语言' words=jieba.lcut(txt) #精确分词 words_all=jieba.lcut(txt,cut_al ...

  4. PAT(B) 1033 旧键盘打字(C) 字符

    题目链接:1033 旧键盘打字 (20 point(s)) 题目描述 旧键盘上坏了几个键,于是在敲一段文字的时候,对应的字符就不会出现.现在给出应该输入的一段文字.以及坏掉的那些键,打出的结果文字会是 ...

  5. WUSTOJ 1338: The minimum square sum(Java)

    题目链接:1338: The minimum square sum Description Given a prime p(p<108), you are to find min{x2+y2}, ...

  6. vagrant root 登录虚拟机

    这个问题本来觉得是个特别简单的问题,昨天弄的时候折腾了半晚上.所以打算记录下过程,主要也被网上的各种信息误导了. 1 先看下我这vagrant配置信息 Vagrant.configure(" ...

  7. 【flume】5.采集日志进入hbase

    设置我们的flume配置信息 # Licensed to the Apache Software Foundation (ASF) under one # or more contributor li ...

  8. Android--Fragment嵌套的问题

    项目中遇到Fragment嵌套应用的问题 子Fragment中要用getChildFragmentManager()方法获取FragmentManager,否则会出问题!

  9. Spring Boot使用@Value注解获取配置文件中的属性

    获取配置文件的内容——

  10. Java开发Hbase示例

    Java开发Hbase示例 使用Hbase操作数据 package com.sunteng.clickidc.test; import java.io.IOException; import java ...