Golang:使用反射获取指向结构的指针

发布于 2025-01-17 07:54:41 字数 851 浏览 0 评论 0原文

我正在尝试编写递归遍历结构并跟踪指向其所有字段的指针以进行基本分析(大小、引用数量等)的代码。但是,我遇到了一个问题,我似乎无法通过反射来为我提供指向纯结构的指针。我有以下代码作为示例:

type foo struct {
    A    *bar
    data []int8
}

type bar struct {
    B       *foo
    ptrData *[]float64
}

func main() {
    dataLen := 32
    refData := make([]float64, dataLen)

    fooObj := foo{data: make([]int8, dataLen)}
    barObj := bar{
        B:       &fooObj,
        ptrData: &refData,
    }
    fooObj.A = &barObj

    fooVal := reflect.ValueOf(fooObj)
    _ := fooVal.Addr().Pointer() // fails
    _ := fooVal.Pointer() // fails

    // More analysis code after this
}

如果我想遍历 fooObj,那就没问题,直到我输入 barObj,此时我再次遇到 fooObj.因为我没有办法获取初始 fooObj 遇到的指针,所以我最终遍历了 fooObj 两次,直到点击 barObj第二次并退出递归。知道如何使用反射获取结构体的指针吗?

I'm trying to write code that recursively traverses a struct and keeps track of pointers to all its fields to do basic analysis (size, number of references, etc). However, I'm running into an issue where I can't seem to get reflection to give me the pointer to a pure struct. I have the following code as an example:

type foo struct {
    A    *bar
    data []int8
}

type bar struct {
    B       *foo
    ptrData *[]float64
}

func main() {
    dataLen := 32
    refData := make([]float64, dataLen)

    fooObj := foo{data: make([]int8, dataLen)}
    barObj := bar{
        B:       &fooObj,
        ptrData: &refData,
    }
    fooObj.A = &barObj

    fooVal := reflect.ValueOf(fooObj)
    _ := fooVal.Addr().Pointer() // fails
    _ := fooVal.Pointer() // fails

    // More analysis code after this
}

If I wanted to traverse fooObj, that would be fine until I entered barObj at which point I again encounter fooObj. Because I don't have a way to get the pointer for the initial fooObj encounter, I end up traversing fooObj twice until I hit barObj the second time and exit the recursion. Any idea how to get a struct's pointer using reflection?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

够钟 2025-01-24 07:54:41
package main

import (
    "reflect"
    "fmt"
)

type foo struct {
    A    *bar
    data []int8
}

type bar struct {
    B       *foo
    ptrData *[]float64
}

func main() {
    dataLen := 32
    refData := make([]float64, dataLen)
    
    // allocate here, now value has a pointer
    fooObj := &foo{data: make([]int8, dataLen)} 
    barObj := bar{
        B:       fooObj,
        ptrData: &refData,
    }
    fooObj.A = &barObj

    fooVal := reflect.ValueOf(fooObj)
    fmt.Println(fooVal.Pointer()) // succeeds

    // More analysis code after this
}

Addr 返回一个代表 v 地址的指针值。如果 CanAddr() 返回 false,则会出现混乱。 Addr 通常用于获取指向结构体字段或切片元素的指针,以便调用需要指针接收器的方法。

package main

import (
    "reflect"
    "fmt"
)

type foo struct {
    A    *bar
    data []int8
}

type bar struct {
    B       *foo
    ptrData *[]float64
}

func main() {
    dataLen := 32
    refData := make([]float64, dataLen)
    
    // allocate here, now value has a pointer
    fooObj := &foo{data: make([]int8, dataLen)} 
    barObj := bar{
        B:       fooObj,
        ptrData: &refData,
    }
    fooObj.A = &barObj

    fooVal := reflect.ValueOf(fooObj)
    fmt.Println(fooVal.Pointer()) // succeeds

    // More analysis code after this
}

Addr returns a pointer value representing the address of v. It panics if CanAddr() returns false. Addr is typically used to obtain a pointer to a struct field or slice element in order to call a method that requires a pointer receiver.

晚风撩人 2025-01-24 07:54:41

如果可能的话,这需要一个值的指针。

package main

import "reflect"
import "fmt"

func main() {
    val := new(int)
    slice := []int{}
    local := 10
    fn := func() {}

    fmt.Println(PointerOf(val))
    fmt.Println(PointerOf(slice))
    fmt.Println(PointerOf(&local))
    fmt.Println(PointerOf(fn))
    fmt.Println(PointerOf(3))
}

func PointerOf(value any) (p uintptr, ok bool) {
    rValue := reflect.ValueOf(value)
    
    if(rValue.Kind() == reflect.Pointer || rValue.Kind() == reflect.Slice || rValue.Kind() == reflect.Func) {
        return rValue.Pointer(), true
    }    

    if(rValue.CanAddr()) {
        return rValue.Addr().Pointer(), true
    }

    return
}

This takes a pointer of a value if it's possible.

package main

import "reflect"
import "fmt"

func main() {
    val := new(int)
    slice := []int{}
    local := 10
    fn := func() {}

    fmt.Println(PointerOf(val))
    fmt.Println(PointerOf(slice))
    fmt.Println(PointerOf(&local))
    fmt.Println(PointerOf(fn))
    fmt.Println(PointerOf(3))
}

func PointerOf(value any) (p uintptr, ok bool) {
    rValue := reflect.ValueOf(value)
    
    if(rValue.Kind() == reflect.Pointer || rValue.Kind() == reflect.Slice || rValue.Kind() == reflect.Func) {
        return rValue.Pointer(), true
    }    

    if(rValue.CanAddr()) {
        return rValue.Addr().Pointer(), true
    }

    return
}
雨轻弹 2025-01-24 07:54:41

正如您所提到的,您的代码在这部分失败

fooVal := reflect.ValueOf(fooObj)
_ = fooVal.Addr().Pointer() // fails
_ = fooVal.Pointer() // fails

In foo struct data 不是指针值。

type foo struct {
    A    *bar
    data []int8
}

您尝试在非指针类型上调用 reflect.Value.Addr().Pointer()reflect.Value.Pointer(),从而导致错误。

您可以通过检查类型是否实际上是 ptr 来防止这种情况

package main

import (
    "reflect"
)

type foo struct {
    A    *bar
    data []int8
}

type bar struct {
    B       *foo
    ptrData *[]float64
}

func main() {
    dataLen := 32
    refData := make([]float64, dataLen)

    fooObj := foo{data: make([]int8, dataLen)}
    barObj := bar{
        B:       &fooObj,
        ptrData: &refData,
    }
    fooObj.A = &barObj

    fooVal := reflect.ValueOf(fooObj)

    if fooVal.Kind().String() == "ptr" {
        _ = fooVal.Addr().Pointer() // ok
        _ = fooVal.Pointer()        // ok

       // More analysis code for pointer types
    } else {
       // More analysis code for non-pointer types
    }
}

As you mentioned, your code fails in this part

fooVal := reflect.ValueOf(fooObj)
_ = fooVal.Addr().Pointer() // fails
_ = fooVal.Pointer() // fails

In foo struct data is not a pointer value.

type foo struct {
    A    *bar
    data []int8
}

You are trying to call reflect.Value.Addr().Pointer() and reflect.Value.Pointer() on a non-pointer type, causing the error.

You can prevent this condition by checking if the type is actually ptr

package main

import (
    "reflect"
)

type foo struct {
    A    *bar
    data []int8
}

type bar struct {
    B       *foo
    ptrData *[]float64
}

func main() {
    dataLen := 32
    refData := make([]float64, dataLen)

    fooObj := foo{data: make([]int8, dataLen)}
    barObj := bar{
        B:       &fooObj,
        ptrData: &refData,
    }
    fooObj.A = &barObj

    fooVal := reflect.ValueOf(fooObj)

    if fooVal.Kind().String() == "ptr" {
        _ = fooVal.Addr().Pointer() // ok
        _ = fooVal.Pointer()        // ok

       // More analysis code for pointer types
    } else {
       // More analysis code for non-pointer types
    }
}
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文