欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

初探GO中unsafe包的使用

 更新時(shí)間:2023年08月27日 14:59:02   作者:今天撿到一百塊錢  
unsafe是Go語(yǔ)言標(biāo)準(zhǔn)庫(kù)中的一個(gè)包,提供了一些不安全的編程操作,本文將深入探討Go語(yǔ)言中的unsafe包,介紹它的使用方法和注意事項(xiàng),感興趣的可以了解下

示例代碼

package unsafe
import (
   "errors"
   "reflect"
   "unsafe"
)
type UnsafeAccessor struct {
   // fields 保存結(jié)構(gòu)體中的字段信息
   // {"Name": {"offset": 1, "typ": "String"}, "Age": {"offset": 6, "typ": "Int"}}
   fields map[string]Field
   // address 結(jié)構(gòu)體的起始地址
   address unsafe.Pointer
}
// NewUnsafeAccessor 初始化結(jié)構(gòu)體
func NewUnsafeAccessor(entity any) *UnsafeAccessor {
   typ := reflect.TypeOf(entity).Elem()
   numField := typ.NumField()
   fields := make(map[string]Field, numField)
   for i := 0; i < numField; i++ {
      fd := typ.Field(i)
      fields[fd.Name] = Field{
         offset: fd.Offset,
         typ:    fd.Type,
      }
   }
   val := reflect.ValueOf(entity)
   return &UnsafeAccessor{
      fields:  fields,
      address: val.UnsafePointer(),
   }
}
// Field 讀取字段上的數(shù)據(jù)
func (a *UnsafeAccessor) Field(field string) (any, error) {
   fd, ok := a.fields[field]
   if !ok {
      return nil, errors.New("非法字段")
   }
   // address := unsafe.Pointer(a.address + fd.offset)
   address := unsafe.Pointer(uintptr(a.address) + fd.offset)
   // 已知字段類型,如下操作
   // return *(*int8)(address), nil
   // 未知字段類型,如下操作
   return reflect.NewAt(fd.typ, address).Elem().Interface(), nil
}
// SetField 向字段上寫入數(shù)據(jù)
func (a *UnsafeAccessor) SetField(field string, value any) error {
   fd, ok := a.fields[field]
   if !ok {
      return errors.New("非法字段")
   }
   // address := unsafe.Pointer(a.address + fd.offset)
   address := unsafe.Pointer(uintptr(a.address) + fd.offset)
   // 已知字段類型賦值
   // *(*int8)(address) = value.(int8)
   // 未知字段類型賦值
   reflect.NewAt(fd.typ, address).Elem().Set(reflect.ValueOf(value))
   return nil
}
type Field struct {
   // offset 字段的偏移量
   offset uintptr
   // typ 字段類型
   typ reflect.Type
}

測(cè)試代碼

package unsafe
import (
   "github.com/stretchr/testify/assert"
   "testing"
)
func TestUnsafeAccessor_Field(t *testing.T) {
   testCases := []struct {
      name    string
      entity  any
      field   string
      wantRes any
      wantErr error
   }{
      {
         name: "query field value already know field type",
         entity: &struct {
            Name string
            Age  int8
         }{
            Name: "Tom",
            Age:  19,
         },
         field:   "Age",
         wantRes: int8(19),
         wantErr: nil,
      },
      {
         name: "query field value don't know field type",
         entity: &struct {
            Name string
            Age  int8
         }{
            Name: "Tom",
            Age:  19,
         },
         field:   "Age",
         wantRes: int8(19),
         wantErr: nil,
      },
   }
   for _, tc := range testCases {
      t.Run(tc.name, func(t *testing.T) {
         res, err := NewUnsafeAccessor(tc.entity).Field(tc.field)
         assert.Equal(t, tc.wantErr, err)
         if err != nil {
            return
         }
         assert.Equal(t, tc.wantRes, res)
      })
   }
}
func TestUnsafeAccessor_SetField(t *testing.T) {
   testCases := []struct {
      name    string
      entity  any
      field   string
      value   any
      wantRes any
      wantErr error
   }{
      {
         name: "set field value already know field type",
         entity: &struct {
            Name string
            Age  int8
         }{
            Name: "Tom",
         },
         field: "Age",
         value: int8(20),
         wantRes: &struct {
            Name string
            Age  int8
         }{
            Name: "Tom",
            Age:  20,
         },
         wantErr: nil,
      },
      {
         name: "set field value don't know field type",
         entity: &struct {
            Name string
            Age  int8
         }{
            Name: "Tom",
            Age:  19,
         },
         field: "Name",
         value: "Jack",
         wantRes: &struct {
            Name string
            Age  int8
         }{
            Name: "Jack",
            Age:  19,
         },
         wantErr: nil,
      },
   }
   for _, tc := range testCases {
      t.Run(tc.name, func(t *testing.T) {
         err := NewUnsafeAccessor(tc.entity).SetField(tc.field, tc.value)
         assert.Equal(t, tc.wantErr, err)
         if err != nil {
            return
         }
         assert.Equal(t, tc.wantRes, tc.entity)
      })
   }
}

錯(cuò)誤總結(jié)

reflect: call of reflect.Value.UnsafePointer on struct Value 這是由于reflect.ValueOf(entity).UnsafePointer()引起的,因?yàn)閁nsafePointer方法只能由entity是Chan、Func、Map、Pointer、Slice、UnsafePointer調(diào)用。這點(diǎn)大家自定去看UnsafePointer的內(nèi)部實(shí)現(xiàn)即可。所以我們得傳入一個(gè)結(jié)構(gòu)體指針。

reflect.NumField of non-struct type *Struct 只是處理上一個(gè)問(wèn)題所引出的另一個(gè)問(wèn)題,因?yàn)槲覀儌魅肓艘粋€(gè)結(jié)構(gòu)體指針,而指針結(jié)構(gòu)體本身并沒有什么字段信息,當(dāng)我們反射指針結(jié)構(gòu)體的時(shí)候,需要使用Elem方法找到指針結(jié)構(gòu)體最終的結(jié)構(gòu)體信息【可能有點(diǎn)繞】所以我們得用reflect.TypeOf().Elem()操作

疑問(wèn)總結(jié)

Accessor結(jié)構(gòu)體中的偏移量類型和Field的偏移量類型怎么不一樣? Accessor中的記錄偏移量的類型是unsafe.Pointer,F(xiàn)ield中記錄地址偏移量的類型是uintptr類型。而來(lái)在大部分情況下作用是一樣的。只有一點(diǎn)點(diǎn)區(qū)別。

舉例來(lái)說(shuō)就像:uintptr像int類型,unsafe.Pointer像*int類型,【用string、bool距離也可以】

int和*int就是我們developer層面來(lái)說(shuō)的,而uintptr和unsafe.Pointer是在GO內(nèi)部層面來(lái)說(shuō)的。

在垃圾回收前后,int類型的地址可能會(huì)發(fā)生變化,而*int就不是這樣。*int本身保存的就是數(shù)據(jù)地址,不論有沒有觸發(fā)GC。或者說(shuō),GO內(nèi)部層面會(huì)幫我們處理好對(duì)*int的維護(hù)。

例子講完了,回到我們的正題,uintptr存儲(chǔ)的是實(shí)打?qū)嵉臄?shù)據(jù)信息,而unsafe.Pointer存儲(chǔ)的是數(shù)據(jù)的指針,也就是地址

什么時(shí)候用unintptr,什么時(shí)候用unsafe。Pointer呢? 拋出結(jié)論,優(yōu)先使用unsafe.Pointer。其實(shí)這個(gè)也可以這樣想,你什么時(shí)候用int類型,什么時(shí)候用*int類型呢?

在我們的例子中,我們是在Accessor中用的是unsafe.Pointer類型,為什么呢?因?yàn)樗枰涗洰?dāng)前結(jié)構(gòu)體的起始地址的偏移量;而在Field中用的uintptr,這是一個(gè)相對(duì)的概念。它是相對(duì)于Accessor中的偏移量來(lái)說(shuō)的。

  • *(*int8)(address)是個(gè)什么鬼? 首先這肯定是一個(gè)斷言操作,不用懷疑;其次,address是一個(gè)unsafe.Pointer類型。我們斷言address是*(*int8)類型。具體看unsafe.Pointer的源碼。清楚講了。
  • reflect.NewAt(fd.Typ, address).Elem().Interface()是什么操作 reflect.NewAt()是將偏移量定義成fd.typ類型。

因?yàn)閞eflect.NewAt方法返回的是一個(gè)指針Value類型,所以需要用Elem獲取其本質(zhì)的結(jié)構(gòu)體信息

因?yàn)檫@里我們需要具體的值。但是Value具體的數(shù)據(jù)是存儲(chǔ)在其內(nèi)部的ptr中,所以需要用Interface將ptr取出來(lái),返回的是一個(gè)any類型。

reflect.ValueOf(entity).UnsafePointerreflect.ValueOf(entity).UnsafeAddr的區(qū)別

到此這篇關(guān)于初探GO中unsafe包的使用的文章就介紹到這了,更多相關(guān)GO unsafe內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • Go?interface?接口的最佳實(shí)踐經(jīng)驗(yàn)分享

    Go?interface?接口的最佳實(shí)踐經(jīng)驗(yàn)分享

    go?的接口在go的編程里面用的十分頻繁,尤其是空接口的使用,因?yàn)橛辛私涌冢攀沟肎o語(yǔ)言變得異常的強(qiáng)大,今天給大家介紹下Go?interface?接口的最佳實(shí)踐經(jīng)驗(yàn)分享,感興趣的朋友一起看看吧
    2022-04-04
  • Go使用proto3的踩坑實(shí)戰(zhàn)記錄

    Go使用proto3的踩坑實(shí)戰(zhàn)記錄

    這篇文章主要給大家介紹了關(guān)于Go使用proto3的踩坑記錄,文中通過(guò)實(shí)例代碼介紹的非常詳細(xì),對(duì)大家學(xué)習(xí)或者會(huì)用Go語(yǔ)言具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下
    2023-02-02
  • Go框架自動(dòng)化工具Beego使用詳解

    Go框架自動(dòng)化工具Beego使用詳解

    這篇文章主要為大家介紹了Go框架自動(dòng)化工具Beego使用詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2023-12-12
  • Golang實(shí)現(xiàn)IP地址轉(zhuǎn)整數(shù)的方法詳解

    Golang實(shí)現(xiàn)IP地址轉(zhuǎn)整數(shù)的方法詳解

    在 Go 語(yǔ)言中,將 IP 地址轉(zhuǎn)換為整數(shù)涉及到解析 IP 地址并處理其字節(jié)表示,本文給大家介紹了Golang實(shí)現(xiàn)IP地址轉(zhuǎn)整數(shù)的方法,文中有詳細(xì)的代碼示例供大家參考,需要的朋友可以參考下
    2024-02-02
  • 淺析Go語(yǔ)言中閉包的定義與使用

    淺析Go語(yǔ)言中閉包的定義與使用

    閉包是編程語(yǔ)言中的一個(gè)重要概念,它允許函數(shù)不僅僅是獨(dú)立的代碼塊,還可以攜帶數(shù)據(jù)和狀態(tài),本文將深入探討閉包的定義、用途和注意事項(xiàng),以及如何正確使用閉包,有需要的可以參考下
    2023-09-09
  • Go?并發(fā)編程協(xié)程及調(diào)度機(jī)制詳情

    Go?并發(fā)編程協(xié)程及調(diào)度機(jī)制詳情

    這篇文章主要介紹了Go并發(fā)編程協(xié)程及調(diào)度機(jī)制詳情,協(xié)程是Go語(yǔ)言最大的特色之一,goroutine的實(shí)現(xiàn)其實(shí)是通過(guò)協(xié)程,更多相關(guān)內(nèi)容需要的朋友可以參考一下
    2022-09-09
  • Golang技巧之重試機(jī)制詳解

    Golang技巧之重試機(jī)制詳解

    重試機(jī)制是一種在程序執(zhí)行過(guò)程中出現(xiàn)錯(cuò)誤后重新嘗試執(zhí)行程序的一種機(jī)制,可以減少程序運(yùn)行過(guò)程中出現(xiàn)的錯(cuò)誤,從而提高程序的可靠性,本文就來(lái)講講Golang中是如何實(shí)現(xiàn)重試機(jī)制的吧
    2023-05-05
  • 詳解golang中Context超時(shí)控制與原理

    詳解golang中Context超時(shí)控制與原理

    Context本身的含義是上下文,我們可以理解為它內(nèi)部攜帶了超時(shí)信息、退出信號(hào),以及其他一些上下文相關(guān)的值,本文給大家詳細(xì)介紹了golang中Context超時(shí)控制與原理,文中有相關(guān)的代碼示例供大家參考,需要的朋友可以參考下
    2024-01-01
  • 如何利用Go語(yǔ)言實(shí)現(xiàn)LRU?Cache

    如何利用Go語(yǔ)言實(shí)現(xiàn)LRU?Cache

    這篇文章主要介紹了如何利用Go語(yǔ)言實(shí)現(xiàn)LRU?Cache,LRU是Least?Recently?Used的縮寫,是一種操作系統(tǒng)中常用的頁(yè)面置換算法,下面我們一起進(jìn)入文章了解更多內(nèi)容吧,需要的朋友可以參考一下
    2022-03-03
  • 使用go語(yǔ)言實(shí)現(xiàn)查找兩個(gè)數(shù)組的異同操作

    使用go語(yǔ)言實(shí)現(xiàn)查找兩個(gè)數(shù)組的異同操作

    這篇文章主要介紹了使用go語(yǔ)言實(shí)現(xiàn)查找兩個(gè)數(shù)組的異同操作,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧
    2020-12-12

最新評(píng)論