英文:
Copy slice with reflect and unsafe package
问题
我理解你的问题。你正在尝试使用unsafe
包来进行一些教育目的的操作,但是你意识到这样做是不安全的。
你的想法是复制一个结构体中的切片字段,使用反射包和unsafe.Pointer
进行复制,然后修改并替换原始切片。看起来新的切片被创建了,并且具有正确的容量/长度,并且包含了GoodForSale
类型的实例。但是该实例的所有字段(Name、Price和Qnty)的值都是错误的。所以我猜测你在处理指针和获取垃圾数据方面做错了什么。
你想知道如何使这个想法正确工作。
英文:
I understand that usage of unsafe
package of golang is unsafe, but I'm doing it only for education purposes.
The idea is to copy a slice field of structure, copy it with reflect package and unsafe.pointer, modify and replace the original slice with new one.
And it looks like new slice is created and has correct capacity/length, and it contains an instance of GoodForSale
type. But all fields of that instance (Name, Price and Qnty) have wrong values. So I suppose that I'm doing something wrong with pointers and getting garbade data:
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)
}
It looks like I'm missing something about how pointers work here. But how can I make this idea work correct?
Here is a go playground url: https://play.golang.org/p/SKtJWe9RVEU
答案1
得分: 3
问题在这里:
pt1 := unsafe.Pointer(&lead.Items) // 指向切片的指针,也就是“切片头”
copyItems := &reflect.SliceHeader{
Data: uintptr(pt1), // 尝试将其用作指向实际数据的指针。
"Data" 需要一个指向实际数据开始位置的指针,但你给的是指向切片本身的指针。
这是获取指向切片数据的正确方法:
pt1 := unsafe.Pointer(&lead.Items[0]) // 指向切片引用的第一个元素的指针
请注意,如果 len(lead.Items) == 0
,这将引发 panic。在这种情况下,你应该使用 unsafe.Pointer(nil)
。
你还可以通过将原始切片强制转换为 reflect.SliceHeader
来获取数据指针(以及长度和容量),如下所示:
copyHeader := *(*reflect.SliceHeader)(unsafe.Pointer(&lead.Items))
items := *(*[]GoodForSale)(unsafe.Pointer(©Header))
但在这一点上,我们实际上只是制造了一个复杂和 "不安全" 的版本,相当于:
items := lead.Items
英文:
The problem is here:
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.
"Data" needs a pointer to where the actual data starts, but you're giving a pointer to the slice itself.
This is the correct way to get a pointer to the slice's data:
pt1 := unsafe.Pointer(&lead.Items[0]) // pointer to the first element referenced by the slice
Note that this will panic if len(lead.Items) == 0
. In that case, you should use unsafe.Pointer(nil)
.
You could also get the data pointer (along with len and cap) from the original slice by casting it as a reflect.SliceHeader
like this:
copyHeader := *(*reflect.SliceHeader)(unsafe.Pointer(&lead.Items))
items := *(*[]GoodForSale)(unsafe.Pointer(&copyHeader))
but at this point we've essentially just made a convoluted and "unsafe" version of this:
items := lead.Items
答案2
得分: 1
根据 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[0]))
items := *(*[]GoodForSale)(unsafe.Pointer(©Header))
fmt.Println(pt1)
fmt.Println(items[0].Name)
}
输出:
0xc00000c080
Iphone 6
英文:
Based on the inputs from Hymns For Disco modifying your code by providing the element of slice instead of whole slice to the pointer.
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)
}
Output:
0xc00000c080
Iphone 6
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论