gpt4 book ai didi

function - *(*uintptr) 和 **(**uintptr) 有什么区别

转载 作者:行者123 更新时间:2023-12-01 22:56:14 26 4
gpt4 key购买 nike

在Go的runtime/proc.go中,有一段代码如下所示:

// funcPC returns the entry PC of the function f.
// It assumes that f is a func value. Otherwise the behavior is undefined.
// CAREFUL: In programs with plugins, funcPC can return different values
// for the same function (because there are actually multiple copies of
// the same function in the address space). To be safe, don't use the
// results of this function in any == expression. It is only safe to
// use the result as an address at which to start executing code.

//go:nosplit
func funcPC(f interface{}) uintptr {
return **(**uintptr)(add(unsafe.Pointer(&f), sys.PtrSize))
}

我不明白的是为什么不使用 *(*uintptr) 而不是 **(**uintptr)?

所以我在下面编写了一个测试程序来弄清楚。

package main

import (
"fmt"
"unsafe"
)


func main(){
fmt.Println()

p := funcPC(test)

fmt.Println(p)

p1 := funcPC1(test)

fmt.Println(p1)

p2 := funcPC(test)

fmt.Println(p2)
}

func test(){
fmt.Println("hello")
}

func funcPC(f func()) uintptr {
return **(**uintptr)(unsafe.Pointer(&f))
}

func funcPC1(f func()) uintptr {
return *(*uintptr)(unsafe.Pointer(&f))
}

enter image description here

p 不等于 p1 的结果让我很困惑。为什么 p 的值不等于 p1 的值,但它们的类型相同?

最佳答案

简介

Go 中的函数值表示函数的代码。从远处看,它是一个指向函数代码的指针。它的作用就像一个指针。

仔细观察,它是一个类似这样的结构(取自runtime/runtime2.go):

type funcval struct {
fn uintptr
// variable-size, fn-specific data here
}

因此,函数值将指向函数代码的指针作为其第一个字段,我们可以取消引用以获取函数的代码。

解释你的例子

要获取函数(代码)的地址,您可以使用反射:

fmt.Println("test() address:", reflect.ValueOf(test).Pointer())

为了验证我们获得了正确的地址,我们可以使用 runtime.FuncForPC() .

这给出了与您的 funcPC() 函数相同的值。请参阅此示例:

fmt.Println("reflection test() address:", reflect.ValueOf(test).Pointer())
fmt.Println("funcPC(test):", funcPC(test))
fmt.Println("funcPC1(test):", funcPC1(test))

fmt.Println("func name for reflect ptr:",
runtime.FuncForPC(reflect.ValueOf(test).Pointer()).Name())

它输出(在 Go Playground 上尝试):

reflection test() address: 919136
funcPC(test): 919136
funcPC1(test): 1357256
func name for reflect ptr: main.test
为什么? 因为函数值本身就是一个指针(它只是与指针的类型不同,但它存储的值是一个指针) 需要取消引用才能获取代码地址强>.

因此,要将其获取到 funcPC() 内的 uintptr (代码地址),您需要做的很简单:

func funcPC(f func()) uintptr {
return *(*uintptr)(f) // Compiler error!
}

当然编译不了,conversion rules不允许将函数值转换为 *uintptr

另一种尝试可能是首先将其转换为 unsafe.Pointer,然后转换为 *uintptr:

func funcPC(f func()) uintptr {
return *(*uintptr)(unsafe.Pointer(f)) // Compiler error!
}

再次强调:转换规则不允许将函数值转换为 unsafe.Pointer。任何指针类型和 uintptr 值都可以转换为 unsafe.Pointer ,反之亦然,但函数值除外。

这就是为什么我们必须有一个指针值作为开始。我们可以得到什么指针值?是的,f的地址:&f。但这不是函数值,而是f参数(局部变量)的地址。所以 &f 示意性地不仅仅是一个指针,它是一个指向指针的指针(两者都需要取消引用)。我们仍然可以将其转换为 unsafe.Pointer (因为任何指针值都符合此条件),但它不是函数值(作为指针),而是指向它的指针。

而且我们需要函数值的代码地址,因此我们必须使用**uintptr来转换unsafe.Pointer值,并且我们必须使用2取消引用以获取地址(而不仅仅是 f 中的指针)。

这正是 funcPC1() 给出不同的、意外的、不正确的结果的原因:

func funcPC1(f func()) uintptr {
return *(*uintptr)(unsafe.Pointer(&f))
}

它返回f中的指针,而不是实际的代码地址。

关于function - *(*uintptr) 和 **(**uintptr) 有什么区别,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/59084717/

26 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com