unsafe.Pointer只是单纯的通用指针类型,是用unsafe函数可以提高访问对象的速度

但是go语言同时也提供了一个unsafe.Pointer包,包unsafe提供了下面两条重要的指针相关的功能,unsafe.Pointer 可以和 uintptr 进行相互转换,unsafe.Pointer 可以和 普通指针 进行相互转换,而uintptr是用于指针运算的,unsafe.Pointer只是单纯的通用指针类型,unsafe中,是用unsafe函数可以提高访问对象的速度,或者用cgo之类的方式得到的内存,从一个内存指针构造出Go语言的slice结构相对麻烦一些

图片 2

譬如来讲

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,通过*操作,取值,修改值,随意。

将指针v转成通用指针,再转成int32指针。这里就见到了unsafe.Pointer的功能了,您不可能平素将v转成int32品类的指针,那样将会panic。刚才说了v的地址其实正是它的率先个分子的地点,所以这么些i就很显然指向了v的成员i,通过给i赋值就一定于给v.i赋值了,不过别忘了i只是个指针,要赋值得解援引。

Offsetof重回变量内定属性的偏移量,那几个函数固然接受的是其他类型的变量,可是那一个又多个前提,就是变量假诺三个struct类型,且还不能够平素将那么些struct类型的变量当做参数,只可以将那么些struct类型变量的习性当作参数。

图片 1

  • unsafe.Pointer是一个指针类型,指向的值无法被剖判,类似于C/C++里面包车型大巴,只表达那是叁个指南针,可是指向哪些的不亮堂。
  • uintptr
    是四个整数门类,那一个寸头的肥瘦足以用来积累多少个指针类型数据;那既然是整数类项目,当然就能够对其打开演算了。

八个品类简单介绍

// 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

—-poit

unsafe.Pointer 能够和 uintptr 实行相互转变。

先将ptr强制类型转换为另一种指针,一个针对[1<<10]byte数组的指针,这里数组大小其实是假的。然后用slice操作抽出那个数组的前200个,于是s便是叁个200个因素的slice。

粗略地说go语言的指针类型和C/C++的指针类型用法是同一的,除了出去安全性的虚拟,go语言扩展了部分限制,包蕴如下几条:

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是阳台相关的,在三十几个人系统下大小是4bytes,在六17位系统下是8bytes。

要得到值类型的大小,须要对指针变量进行取值:

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

import (
 "poit/p"
 "unsafe"
)

func main() {
 var v *p.V = new(p.V)
 var i *int32 = (*int32)(unsafe.Pointer(v))
 *i = int32(98)
 var j *int64 = (*int64)(unsafe.Pointer(uintptr(unsafe.Pointer(v)) + uintptr(unsafe.Sizeof(int32(0)))))
 *j = int64(763)
 v.PutI()
 v.PutJ()
}

是int的一个别称,不过golang中,对ArbitraryType赋予了突出的意义,

struct Slice

go编译器会对具有的指针类型调换都报错,也对指针的运算报错了,如下:

归咎示范

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
语言中的指针偏移操作。能够见见,这种不安全的操作使得大家能够在别的地点一贯访谈结构体中未公开的分子,只要能取得这么些结构体变量的地点。

等等,好像跟大家想像的不一样样。来手动总括一下:b是byte类型,占1个字节;i是int32门类,占4个字节;j是int64类型,占8个字节,1+4+8=13。那是怎么回事呢?那是因为发生了对齐。在struct中,它的对齐值是它的分子中的最大对齐值。每一种成员类型都有它的对齐值,能够用unsafe.Alignof方法来总括,譬如unsafe.Alignof(w.b)就足以获得b在w中的对齐值。同理,我们可以总计出w.b的对齐值是1,w.i的对齐值是4,w.j的对齐值也是4。假使你感觉w.j的对齐值是8那就错了,所以大家日前的代码能正确实行(试想一下,就算w.j的对齐值是8,那前边的赋值代码就不平时了。也正是说后面包车型客车赋值中,假如v.j的对齐值是8,那么v.i跟v.j之间应当有4个字节的填充。所以得到正确的对齐值是很首要的)。对齐值最小是1,那是因为存款和储蓄单元是以字节为单位。所以b就在w的首地址,而i的对齐值是4,它的仓库储存地方必需是4的翻番,由此,在b和i的中间有3个填充,同理j也急需对齐,但因为i和j之间无需填写,所以w的Sizeof值应该是13+3=16。假诺要经过unsafe来对w的七个村办成员赋值,b的赋值同前,而i的赋值则供给跳过3个字节,也正是测算偏移量的时候多跳过3个字节,同理j的撼动能够通过简单的数学生运动算就能够获取。

也便是说 unsafe.Pointer
是桥梁,能够让随便档案的次序的指针达成互相之间转变,也得以将轻巧档期的顺序的指针转变为
uintptr 实行指针运算。

ptr := unsafe.Pointer(&s[0])

$ go build main.go# command-line-arguments./main.go:14: cannot convert p2 (type *int32) to type *int./main.go:15: cannot convert p3 (type *int64) to type *int./main.go:17: cannot convert p1 (type *int) to type *int32./main.go:18: cannot convert p3 (type *int64) to type *int32./main.go:20: cannot convert p1 (type *int) to type *int64./main.go:21: cannot convert p2 (type *int32) to type *int64./main.go:23: invalid operation: p1 + 8 (mismatched types *int and int)

参照他事他说加以考察资料:

  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

好吧,未来貌视一切就绪了,来打字与印刷下:

修改另外包中的构造体私有字段

package mainimport ( "fmt" "reflect" "strings" "unsafe")func main() { // 创建一个 strings 包中的 Reader 对象 // 它有三个私有字段:s string、i int64、prevRune int sr := strings.NewReader // 此时 sr 中的成员是无法修改的 fmt.Println // 但是我们可以通过 unsafe 来进行修改 // 先将其转换为通用指针 p := unsafe.Pointer // 获取结构体地址 up0 := uintptr // 确定要修改的字段(这里不能用 unsafe.Offsetof 获取偏移量,因为是私有字段) if sf, ok := reflect.TypeOf.FieldByName; ok { // 偏移到指定字段的地址 up := up0 + sf.Offset // 转换为通用指针 p = unsafe.Pointer // 转换为相应类型的指针 pi :=  // 对指针所指向的内容进行修改 *pi = 3 // 修改索引 } // 看看修改结果 fmt.Println // 看看读出的是什么 b, err := sr.ReadByte() fmt.Printf("%c, %v\n", b, err)}

// 定义一个和 strings 包中的 Reader 相同的本地结构体type Reader struct { s string i int64 prevRune int}func main() { // 创建一个 strings 包中的 Reader 对象 sr := strings.NewReader // 此时 sr 中的成员是无法修改的 fmt.Println // 我们可以通过 unsafe 来进行修改 // 先将其转换为通用指针 p := unsafe.Pointer // 再转换为本地 Reader 结构体 pR :=  // 这样就可以自由修改 sr 中的私有成员了 .i = 3 // 修改索引 // 看看修改结果 fmt.Println // 看看读出的是什么 b, err := sr.ReadByte() fmt.Printf("%c, %v\n", b, err)}

cap int

相对来讲前面提到的go语言对指针使用的限制,这两条扩大正是对准前边对指针使用的限量而定制扩张的。

示例

size=16

  • type Pointer *ArbitraryType

图片 2

package mainimport func main() { i1 := int i2 := int32 i3 := int64 p1 := &i1 p2 := &i2 p3 := &i3 p1 =  p1 =  p2 =  p2 =  p3 =  p3 =  p1 = p1 + 8 fmt.Printf("p1=%p,p2=%p,p3=%p\n", p1, p2, p3);}

反射包的对齐方法

反射包也是有一点点方法可用以总计对齐值:

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

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

unsafe.Pointer其实正是类似C的void
*,在golang中是用于各样指针互相转变的桥梁。uintptr是golang的放权类型,是能积累指针的整型,uintptr的平底类型是int,它和unsafe.Pointer可互相转变。uintptr和unsafe.Pointer的不同正是:unsafe.Pointer只是可是的通用指针类型,用于转移差异类别指针,它不可以涉足指针运算;而uintptr是用于指针运算的,GC
不把 uintptr 当指针,也正是说 uintptr
无法具备对象,uintptr类型的靶子会被回收。golang的unsafe包很有力,基本上非常少会去用它。它能够像C同样去操作内部存款和储蓄器,但鉴于golang不扶助直接进行指针运算,所以用起来稍显麻烦。

unsafe.Pointer 能够和 普通指针 举办互动调换。

addr uintptr

$ go build && ./main *p0=11,*p4=22

结构体的对齐值

若是我们计算的是结构体的对齐值并不是某些字段或许基本项目,那么值会是有个别呢?

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

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

变动结构,验证一下:

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

总结

func Alignof(x ArbitraryType) uintptrfunc Offsetof(x ArbitraryType) uintptrfunc Sizeof(x ArbitraryType) uintptrtype ArbitraryType inttype Pointer *ArbitraryType

实际上slice在Go的运营时库中便是一个C语言动态数组的兑现,在$GOROOT/src/pkg/runtime/runtime.h中能够看出它的定义:

至于unsafe.Pointer的详尽表达

对齐

在上头的例子中,*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。

v.PutI()
v.PutJ()

unsafe包提供了拜见底层内存的主意。是用unsafe函数可以巩固访谈对象的速度。经常用于对天意组的遍历。

出于slice是分歧于指针的多字长结构,分割操作并无需分配内存,以至尚未普通被保存在堆中的slice尾部。这种代表方法使slice操作和在C中传递指针、长度对同一廉价。Go语言最先使用多个针对以上结构的指针来表示slice,不过这么做意味着每一个slice操作都会分配一块新的内存对象。纵然使用了飞快的分配器,依然给垃圾搜集器创设了点不清没有须要的劳作。移除直接援引及分红操作能够让slice充裕廉价,以制止传递显式索引。

唯独go语言同期也提供了三个unsafe.Pointer包,在它的扶植下,一切操作都是立见功用的了。不过请介意,那些操作都以不安全的,因为unsafe包里面提供的函数打破Go类型系统和内部存款和储蓄器管理的哈密机制,需求使用者非常的小心,那也正是其包名叫什么叫做unsafe。

三个函数简单介绍

// 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在内部存款和储蓄器中的实际据有长度

————v.go

  • type ArbitraryType int

这部分:

其一例子,定义了多个int数组,然后定义七个针对第三个要素的指针,接着运算那么些指针,让它指向下一个成分,最终以int64的格式打字与印刷出下二个因素的值。

实际来讲课下main方法的完结:

unsafe中,通过那多个个特别万物的类型,将别的门类都转移过来,然后经过那多少个函数,分别能取长度,偏移量,对齐字节数,就能够在内部存款和储蓄器地址映射中,来回游走。放在c语言中,是或不是,只要给你三个起头地址,你就一下比干到底!!!在golang中,通过unsafe包,你也能够尽情的去纵容

uintgo cap; // allocated number of elements