iis服务器助手广告广告
返回顶部
首页 > 资讯 > 后端开发 > GO >Golang中的unsafe包有什么用
  • 261
分享到

Golang中的unsafe包有什么用

2023-07-05 19:07:55 261人浏览 薄情痞子
摘要

今天小编给大家分享一下golang中的unsafe包有什么用的相关知识点,内容详细,逻辑清晰,相信大部分人都还太了解这方面的知识,所以分享这篇文章给大家参考一下,希望大家阅读完这篇文章后有所收获,下面我们一起来了解一下吧。unsafe 包提

今天小编给大家分享一下golang中的unsafe包有什么用的相关知识点,内容详细,逻辑清晰,相信大部分人都还太了解这方面的知识,所以分享这篇文章给大家参考一下,希望大家阅读完这篇文章后有所收获,下面我们一起来了解一下吧。

unsafe 包提供了一些操作可以绕过 Go 的类型安全检查, 从而直接操作内存地址, 做一些 tricky 操作。示例代码运行环境是 go version go1.18 darwin/amd64

内存对齐

unsafe 包提供了 Sizeof 方法获取变量占用内存大小「不包含指针指向变量的内存大小」, Alignof 获取内存对齐系数, 具体内存对齐规则可以自行 google.

type demo1 struct {   a bool  // 1   b int32 // 4   c int64 // 8}type demo2 struct {   a bool  // 1   c int64 // 8   b int32 // 4}type demo3 struct { // 64 位操作系统, 字长 8   a *demo1 // 8   b *demo2 // 8}func MemAlign() {   fmt.Println(unsafe.Sizeof(demo1{}), unsafe.Alignof(demo1{}), unsafe.Alignof(demo1{}.a), unsafe.Alignof(demo1{}.b), unsafe.Alignof(demo1{}.c)) // 16,8,1,4,8   fmt.Println(unsafe.Sizeof(demo2{}), unsafe.Alignof(demo2{}), unsafe.Alignof(demo2{}.a), unsafe.Alignof(demo2{}.b), unsafe.Alignof(demo2{}.c)) // 24,8,1,4,8   fmt.Println(unsafe.Sizeof(demo3{}))                                                                                                           // 16}                                                                                                         // 16}复制代码

从上面 case 可以看到 demo1 和 demo2 包含相同的属性, 只是定义的属性顺序不同, 却导致变量的内存大小不同。这里是因为发生了内存对齐。

计算机在处理任务时, 会按照特定的字长「例如:32 位操作系统, 字长为 4; 64 位操作系统, 字长为 8」为单位处理数据。那么, 在读取数据的时候也是按照字长为单位。例如: 对于 64 位操作系统, 程序一次读取的字节数为 8 的倍数。下面是 demo1 在非内存对齐和内存对齐下的布局:

非内存对齐:

变量 c 会被放在不同的字长里面, cpu 在读取的时候需要同时读取两次, 同时对两次的结果做处理, 才能拿到 c 的值。这种方式虽然节省了内存空间, 但是会增加处理时间。

内存对齐:

内存对齐采用了一种方案, 可以避免同一个非内存对齐的这种情况, 但是会额外占用一些空间「空间换时间」。具体内存对齐规则可以自行 google。

Golang中的unsafe包有什么用

Unsafe Pointer

在 go 中可以声明一个指针类型, 这里的类型是 safe pointer, 即要明确指针指向的类型, 如果类型不匹配将会在编译时报错。如下面的示例, 编译器会认为 MyString 和 string 是不同的类型, 无法进行赋值。

func main() {   type MyString string   s := "test"   var ms MyString = s // Cannot use 's' (type string) as the type MyString   fmt.Println(ms)}

那有没有一种类型, 可以指向任意类型的变量呢?可以使用 unsfe.Pointer, 它可以指向任意类型的变量。通过Pointer 的声明, 可以知道它是一个指针类型, 指向变量所在的地址。具体的地址对应的值可以通过 uinptr 进行转换。Pointer 有以下四种特殊的操作:

  • 任意类型的指针都可以转换成 Pointer 类型

  • Pointer 类型的变量可以转换成任意类型的指针

  • uintptr 类型的变量可以转换成 Pointer 类型

  • Pointer 类型的变量可以转换成 uintprt 类型

type Pointer *ArbitraryType// uintptr is an integer type that is large enough to hold the bit pattern of// any pointer.type uintptr uintptrfunc main() {   d := demo1{true, 1, 2}   p := unsafe.Pointer(&d)                // 任意类型的指针可以转换为 Pointer 类型   pa := (*demo1)(p)                      // Pointer 类型变量可以转换成 demo1 类型的指针   up := uintptr(p)                       // Pointer 类型的变量可以转换成 uintprt 类型   pu := unsafe.Pointer(up)               // uintptr 类型的变量可以转换成 Pointer 类型; 当 GC 时, d 的地址可能会发生变更, 因此, 这里的 up 可能会失效   fmt.Println(d.a, pa.a, (*demo1)(pu).a) // true true true}

Pointer 的六种使用方式

在官方文档中给出了 Pointer 的六种使用姿势。

  1. 通过 Pointer 将 *T1 转换为 *T2

Pointer 直接指向一块内存, 因此可以将这块内存地址转为任意类型。这里需要注意, T1 和 T2 需要有相同的内存布局, 会有异常数据。

func main() {   type myStr string   ms := []myStr{"1", "2"}   //ss := ([]string)(ms) Cannot convert an expression of the type '[]myStr' to the type '[]string'   ss := *(*[]string)(unsafe.Pointer(&ms)) // 将 pointer 指向的内存地址直接转换成 *[]string   fmt.Println(ms, ss)}

Golang中的unsafe包有什么用

如果 T1 和 T2 的内存布局不同, 会发生什么呢?在下面的示例子中, demo1 和 demo2 虽然包含相同的结构体, 由于内存对齐, 导致两者是不同的内存布局。将 Pointer 转换时, 会从 demo1 的地址开始读取 24「sizeof」 个字节, 按照demo2 内存对齐规则进行转换, 将第一个字节转换为 a:true, 8-16 个字节转换为 c:2, 16-24 个字节超出了 demo1 的范围, 但仍可以直接读取, 获取了非预期的值 b:17368000。

type demo1 struct {   a bool  // 1   b int32 // 4   c int64 // 8}type demo2 struct {   a bool  // 1   c int64 // 8   b int32 // 4}func main() {   d := demo1{true, 1, 2}   pa := (*demo2)(unsafe.Pointer(&d)) // Pointer 类型变量可以转换成 demo2 类型的指针   fmt.Println(pa.a, pa.b, pa.c) // true, 17368000, 2, }

Golang中的unsafe包有什么用

  1. 将 Pointer 类型转换为 uintptr 类型「不应该将 uinptr 转为 Pointer」

Pointer 是一个指针类型, 可以指向任意变量, 可以通过将 Pointer 转换为 uintptr 来打印 Pointer 指向变量的地址。此外:不应该将 uintptr 转换为 Pointer。如下面的例子: 当发生 GC 时, d 的地址可能会发生变更, 那么 up 由于未同步更新而指向错误的内存。

func main() {   d := demo1{true, 1, 2}   p := unsafe.Pointer(&d)   up := uintptr(p)   fmt.Printf("uintptr: %x, ptr: %p \n", up, &d) // uintptr: c00010c010, ptr: 0xc00010c010   fmt.Println(*(*demo1)(unsafe.Pointer(up)))    // 不允许}

  1. 通过算数计算将 Pointer 转换为 uinptr 再转换回 Pointer

当 Piointer 指向一个结构体时, 可以通过此方式获取到结构体内部特定属性的 Pointer。

func main() {   d := demo1{true, 1, 2}   // 等同于 unsafe.Pointer(&d.b); unsafe.Add(unsafe.Pointer(&d), unsafe.Offsetof(d.b))   pb := unsafe.Pointer(uintptr(unsafe.Pointer(&d)) + unsafe.Offsetof(d.b))   fmt.Println(pb)}

  1. 当调用 syscall.Syscall 的时候, 可以讲 Pointer 转换为 uintptr

前面说过, 由于 GC 会导致变量的地址发生变更, 因此不可以直接处理 uintptr。但是, 在调用 syscall.Syscall 时候可以允许传递一个 uintptr, 这里可以简单理解为是编译器做了特殊处理, 来保证 uintptr 是安全的。

  • 调用方式:

  • syscall.Syscall(SYS_READ, uintptr( fd ), uintptr(unsafe.Pointer(p)), uintptr(n))

下面这种方式是不允许的:

u := uintptr(unsafe.Pointer(p)) // 不应该保存到一个变量上 syscall.Syscall(SYS_READ, uintptr( fd ), u, uintptr(n))

  1. 可以将 reflect.Value.Pointer 或 reflect.Value.UnsafeAddr 的结果「uintptr」转换为 Pointer

在 reflect 包中的 Value.Pointer 和 Value.UnsafeAddr 直接返回了地址对应的值「uintptr」, 可以直接将其结果转为 Pointer

func main() {   d := demo1{true, 1, 2}   // 等同于 unsafe.Pointer(&d.b); unsafe.Add(unsafe.Pointer(&d), unsafe.Offsetof(d.b))   pb := unsafe.Pointer(uintptr(unsafe.Pointer(&d)) + unsafe.Offsetof(d.b))   // up := reflect.ValueOf(&d.b).Pointer(), pc := unsafe.Pointer(up); 不安全, 不应存储到变量中   pc := unsafe.Pointer(reflect.ValueOf(&d.b).Pointer())   fmt.Println(pb, pc)}

  1. 可以将 reflect.SliceHeader 或者 reflect.StringHeader 的 Data 字段与 Pointer 相互转换

SliceHeader 和 StringHeader 其实是 slice 和 string 的内部实现, 里面都包含了一个字段 Data「uintptr」, 存储的是指向 []T 的地址, 这里之所以使用 uinptr 是为了不依赖 unsafe 包。

func main() {   s := "a"   hdr := (*reflect.StringHeader)(unsafe.Pointer(&s)) // *string to *StringHeader   fmt.Println(*(*[1]byte)(unsafe.Pointer(hdr.Data))) // 底层存储的是 utf 编码后的 byte 数组   arr := [1]byte{65}   hdr.Data = uintptr(unsafe.Pointer(&arr))   hdr.Len = len(arr)   ss := *(*string)(unsafe.Pointer(hdr))   fmt.Println(ss) // A   arr[0] = 66   fmt.Println(ss) //B}

应用

string、byte 转换

在业务上, 经常遇到 string 和 []byte 的相互转换。我们知道, string 底层其实也是存储的一个 byte 数组, 可以通过 reflect 直接获取 string 指向的 byte 数组, 赋值给 byte 切片, 避免内存拷贝。

func StrToByte(str string) []byte {   return []byte(str)}func StrToByteV2(str string) (b []byte) {   bh := (*reflect.SliceHeader)(unsafe.Pointer(&b))   sh := (*reflect.StringHeader)(unsafe.Pointer(&str))   bh.Data = sh.Data   bh.Cap = sh.Len   bh.Len = sh.Len   return b}// go test -bench .func BenchmarkStrToArr(b *testing.B) {   for i := 0; i < b.N; i++ {      StrToByte(`{"f": "v"}`)   }}func BenchmarkStrToArrV2(b *testing.B) {   for i := 0; i < b.N; i++ {      StrToByteV2(`{"f": "v"}`)   }}//goos: darwin//goarch: amd64//pkg: GitHub.com/demo/lsafe//cpu: Intel(R) Core(TM) i7-9750H CPU @ 2.60GHz//BenchmarkStrToArr-12            264733503                4.311 ns/op//BenchmarkStrToArrV2-12          1000000000               0.2528 ns/op

通过观察 string 和 byte 的内存布局我们可以知道, 无法直接将 string 转为 []byte 「确实 cap 字段」, 但是可以直接将 []byte 转为 string

Golang中的unsafe包有什么用

func ByteToStr(b []byte) string {   return string(b)}func ByteToStrV2(b []byte) string {   return *(*string)(unsafe.Pointer(&b))}// go test -bench .func BenchmarkArrToStr(b *testing.B) {   for i := 0; i < b.N; i++ {      ByteToStr([]byte{65})   }}func BenchmarkArrToStrV2(b *testing.B) {   for i := 0; i < b.N; i++ {      ByteToStrV2([]byte{65})   }}//goos: darwin//goarch: amd64//pkg: github.com/demo/lsafe//cpu: Intel(R) Core(TM) i7-9750H CPU @ 2.60GHz//BenchmarkArrToStr-12            536188455                2.180 ns/op//BenchmarkArrToStrV2-12          1000000000               0.2526 ns/op

以上就是“Golang中的unsafe包有什么用”这篇文章的所有内容,感谢各位的阅读!相信大家阅读完这篇文章都有很大的收获,小编每天都会为大家更新不同的知识,如果还想学习更多的知识,请关注编程网GO频道。

您可能感兴趣的文档:

--结束END--

本文标题: Golang中的unsafe包有什么用

本文链接: https://www.lsjlt.com/news/354953.html(转载时请注明来源链接)

有问题或投稿请发送至: 邮箱/279061341@qq.com    QQ/279061341

本篇文章演示代码以及资料文档资料下载

下载Word文档到电脑,方便收藏和打印~

下载Word文档
猜你喜欢
  • Golang中的unsafe包有什么用
    今天小编给大家分享一下Golang中的unsafe包有什么用的相关知识点,内容详细,逻辑清晰,相信大部分人都还太了解这方面的知识,所以分享这篇文章给大家参考一下,希望大家阅读完这篇文章后有所收获,下面我们一起来了解一下吧。unsafe 包提...
    99+
    2023-07-05
  • 了解一下Golang中的unsafe包
    在一些底层的库中, 经常会看到使用 unsafe 包的地方。本篇文章就来带大家了解一下Golang中的unsafe包,介绍一下unsafe 包的作用和Pointer的使用方式,希望对大家有所帮助!unsafe 包提供了一些操作可以绕过 go...
    99+
    2023-05-14
    Go 后端
  • Java中Unsafe类有什么用
    小编给大家分享一下Java中Unsafe类有什么用,相信大部分人都还不怎么了解,因此分享这篇文章给大家参考一下,希望大家阅读完这篇文章后大有收获,下面让我们一起去了解一下吧!一、Unsafe类是啥?Java最初被设计为一种安全的受控环境。尽...
    99+
    2023-06-21
  • Golang中的context包有什么用
    Context 包是 Go 语言中用来传递请求范围变量、取消信号以及超时时间的包。它主要用于在 Goroutine 之间传递请求作用...
    99+
    2024-03-13
    Golang
  • 使用golang-unsafe包的注意事项及说明
    目录总结(详细的内容可以往下看)详细内容总结基于golang 15.5 总结(详细的内容可以往下看) 1.不能使用unsafe包里的ArbitraryType类型 2.Pointer...
    99+
    2023-02-10
    golang-unsafe包 golang unsafe 使用golang-unsafe包
  • Go语言中unsafe包怎么使用
    本文小编为大家详细介绍“Go语言中unsafe包怎么使用”,内容详细,步骤清晰,细节处理妥当,希望这篇“Go语言中unsafe包怎么使用”文章能帮助大家解决疑惑,下面跟着小编的思路慢慢深入,一起来学习新知识吧。1. 什么是unsafe包un...
    99+
    2023-07-05
  • Java中Unsafe怎么用
    这篇文章将为大家详细讲解有关Java中Unsafe怎么用,小编觉得挺实用的,因此分享给大家做个参考,希望大家阅读完这篇文章后可以有所收获。Unsafe是什么首先我们说Unsafe类位于rt.jar里面sun.misc包下面,Unsafe翻译...
    99+
    2023-06-25
  • 深入探索Go语言中unsafe包的使用
    目录前言1. 什么是unsafe包2. unsafe.Pointer是什么3. 如何使用unsafe.Pointer来操作内存4. 如何避免unsafe包的内存错误和安全漏洞5. u...
    99+
    2023-05-14
    Go语言 unsafe包使用 Go语言 unsafe包 Go unsafe
  • golang sort包的用法是什么
    sort包是Go语言中用于排序操作的标准包,提供了对切片和用户自定义数据类型的排序功能。 sort包中最常用的函数是sort.Sli...
    99+
    2023-10-26
    golang
  • java中Unsafe类怎么用
    这篇文章主要介绍了java中Unsafe类怎么用,具有一定借鉴价值,感兴趣的朋友可以参考下,希望大家阅读完这篇文章之后大有收获,下面让小编带着大家一起了解一下。Unsafe类使Java拥有了像C语言的指针一样操作内存空间的能力,同时也带来了...
    99+
    2023-06-28
  • Javascript中的闭包有什么用
    这篇文章给大家介绍Javascript中的闭包有什么用,内容非常详细,感兴趣的小伙伴们可以参考借鉴,希望对大家能有所帮助。var array = []; array.lengt...
    99+
    2024-04-02
  • java中Unsafe的使用讲解
    目录1.获取unsafe2.获取unsafe前段时间因为看JUC的源码,里面有大量关于unsafe的操作,所以就来看看了.写点笔记总结下(本文基于jdk1.8): unsafe可以帮...
    99+
    2024-04-02
  • golang包含什么
    golang包含:1、fmt包;2、os包;3、net包;4、time包;5、encoding包;6、sync包;7、container包;8、crypto、math、sort、reflect等,它们提供了加密算法、数学计算、排序和反射等常...
    99+
    2023-07-10
  • JavaScript中闭包有什么用
    这篇文章主要介绍JavaScript中闭包有什么用,文中介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们一定要看完!1. 什么是闭包闭包:函数本身和该函数声明时所处的环境状态的组合。也就是说函数不在其定义的环境中被调用,也能访问定义时所...
    99+
    2023-06-25
  • golang中toolkits包怎么用
    这篇文章主要介绍了golang中toolkits包怎么用,具有一定借鉴价值,感兴趣的朋友可以参考下,希望大家阅读完这篇文章之后大有收获,下面让小编带着大家一起了解一下。在查看 open-falcon 项目源码时,经常会看到其引用了一个类库 ...
    99+
    2023-06-27
  • javascript中闭包有什么作用
    本篇内容介绍了“javascript中闭包有什么作用”的有关知识,在实际案例的操作过程中,不少人都会遇到这样的困境,接下来就让小编带领大家学习一下如何处理这些情况吧!希望大家仔细阅读,能够学有所成!什么是闭...
    99+
    2024-04-02
  • golang的反射有什么用
    在 go 语言中,反射机制允许程序在运行时检查和操作自身类型和值,用途广泛,包括:类型检查、转换和创建新的数据结构元编程和代码生成调试和测试泛型处理第三方库集成 Go 语言中的反射用途...
    99+
    2024-04-21
    python golang
  • golang闭包的概念是什么
    闭包是指一个函数值(函数的指针)与其引用的其环境变量(值)的组合,形成了一个整体。换句话说,闭包是一个包含自由变量(在函数中使用,但...
    99+
    2023-10-20
    golang
  • golang中的代码注释有什么作用
    这篇文章主要介绍了golang中的代码注释有什么作用的相关知识,内容详细易懂,操作简单快捷,具有一定借鉴价值,相信大家阅读完这篇golang中的代码注释有什么作用文章都会有所收获,下面我们一起来看看吧。一、注释的作用在代码编写中,注释起着非...
    99+
    2023-07-05
  • golang闭包的概念及作用是什么
    闭包是指一个函数(匿名函数或者lambda函数)以及其相关的引用环境组合而成的实体。在Go语言中,闭包是一种特殊的匿名函数,它可以访...
    99+
    2024-02-29
    golang
软考高级职称资格查询
编程网,编程工程师的家园,是目前国内优秀的开源技术社区之一,形成了由开源软件库、代码分享、资讯、协作翻译、讨论区和博客等几大频道内容,为IT开发者提供了一个发现、使用、并交流开源技术的平台。
  • 官方手机版

  • 微信公众号

  • 商务合作