微信公众号搜"智元新知"关注
微信扫一扫可直接关注哦!

使用反射和不安全包复制切片

如何解决使用反射和不安全包复制切片

我知道使用 unsafe 包的 golang 是不安全的,但我这样做只是为了教育目的。

思路是复制一个结构体的slice字段,用reflect包和unsafe.pointer复制,修改并用新的slice替换原来的slice。

看起来新切片已创建并且具有正确的容量/长度,并且它包含一个 GoodForSale 类型的实例。但是该实例的所有字段(名称、价格和数量)都有错误的值。所以我想我在指针和获取垃圾数据方面做错了:

package main

import (
    "fmt"
    "reflect"
    "unsafe"
)

type GoodForSale struct {
    Name string
    Price int
    Qnty int
}

type Lead struct {
    ID int
    Name string
    ContactName string
    Budget int
    Items []GoodForSale
    DateTime string
    Status int
    Win bool
}

func main()  {
    lead := &Lead{
        ID:          41,Name:        "Phone",ContactName: "John Smith",Budget:      30000,Items:       []GoodForSale{
            {
                Name:  "Iphone 6",Price: 100,Qnty:  3,},DateTime:    "12.08.2020 11:23:10",Status: 150,Win:         false,}


    //Change Items
    pt1 := unsafe.Pointer(&lead.Items)
    copyItems := &reflect.SliceHeader{
        Data: uintptr(pt1),Cap: cap(lead.Items),Len: len(lead.Items),}


    items := *(*[]GoodForSale)(unsafe.Pointer(copyItems))
    fmt.Println(items[0].Name)

}

我好像遗漏了一些关于指针如何在这里工作的信息。但是我怎样才能使这个想法正确?

这是一个去游乐场网址:https://play.golang.org/p/SKtJWe9RVEU

解决方法

问题出在这里:

pt1 := unsafe.Pointer(&lead.Items) // pointer the slice,a.k.a "slice header"
copyItems := &reflect.SliceHeader{
        Data: uintptr(pt1),// trying to use it as pointer to the actual data.

“数据”需要一个指向实际数据开始位置的指针,但您提供的是一个指向切片本身的指针。

这是获取切片数据指针的正确方法:

pt1 := unsafe.Pointer(&lead.Items[0]) // pointer to the first element referenced by the slice

请注意,如果 len(lead.Items) == 0,这将导致恐慌。在这种情况下,您应该使用 unsafe.Pointer(nil)

您还可以通过将原始切片转换为 reflect.SliceHeader 来从原始切片中获取数据指针(以及 len 和 cap),如下所示:

copyHeader := *(*reflect.SliceHeader)(unsafe.Pointer(&lead.Items))

items := *(*[]GoodForSale)(unsafe.Pointer(&copyHeader))

但在这一点上,我们基本上只是制作了一个复杂且“不安全”的版本:

items := lead.Items
,

基于 Hymns For Disco 的输入,通过向指针提供切片元素而不是整个切片来修改您的代码。

package main

import (
    "fmt"
    "reflect"
    "unsafe"
)

type GoodForSale struct {
    Name  string
    Price int
    Qnty  int
}

type Lead struct {
    ID          int
    Name        string
    ContactName string
    Budget      int
    Items       []GoodForSale
    DateTime    string
    Status      int
    Win         bool
}

func main() {
    lead := &Lead{
        ID:          41,Name:        "Phone",ContactName: "John Smith",Budget:      30000,Items: []GoodForSale{
            {
                Name:  "Iphone 6",Price: 100,Qnty:  3,},DateTime: "12.08.2020 11:23:10",Status:   150,Win:      false,}

    
    pt1 := unsafe.Pointer(&lead.Items[0])

    copyHeader := *(*reflect.SliceHeader)(unsafe.Pointer(&lead.Items))

    items := *(*[]GoodForSale)(unsafe.Pointer(&copyHeader))

    //items := *(*[]GoodForSale)(unsafe.Pointer(copyItems))
    fmt.Println(pt1)
    fmt.Println(items[0].Name)

}

输出:

0xc00000c080
Iphone 6

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。