En Go's runtime/proc.go
, hay un código que se muestra a continuación:
// funcPC devuelve la PC de entrada de la función f.
// Se supone que f es un valor func. De lo contrario, el comportamiento es indefinido.
// CUIDADO: en programas con complementos, funcPC puede devolver diferentes valores
// para la misma función (porque en realidad hay múltiples copias de
// la misma función en el espacio de direcciones). Para estar seguro, no use los
// resultados de esta función en ninguna expresión ==. Solo es seguro
// usar el resultado como una dirección para comenzar a ejecutar el código.
//go:nosplit
func funcPC(f interface{}) uintptr {
return **(**uintptr)(add(unsafe.Pointer(&f), sys.PtrSize))
}
Lo que no entiendo es ¿por qué no usar * (* uintptr) en lugar de ** (** uintptr)?
Entonces escribo un programa de prueba a continuación para descubrir.
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))
}
El resultado de que p no es igual a p1 me confunde. ¿Por qué el valor de p no es igual al valor de p1 mientras su tipo es el mismo?
funcPC(p)
sería el valor de . ¿Cuál es el punto de tener un puntero a un puntero de todos modos?pp
apunta ap
, escribir a*pp
escribep
y leer de*pp
lecturas dep
. Sip
está dentro del alcance, eso es, por supuesto, un poco tonto, ya que podría leer o escribirp
directamente. Pero, ¿quép
pasa si no está dentro del alcance, o qué pasa sipp
apunta a cualquierap
oq
(según la lógica anterior), y le gustaría usar o actualizar el puntero al quepp
apunta?Respuestas:
Introducción
Un valor de función en Go denota el código de la función. Desde lejos, es un puntero al código de la función. Actúa como un puntero.
De un vistazo más de cerca, es una estructura similar a esta (tomada de
runtime/runtime2.go
):Por lo tanto, el valor de una función contiene un puntero al código de la función como su primer campo que podemos desreferenciar para llegar al código de la función.
Explicando tu ejemplo
Para obtener la dirección de una función (código de '), puede usar la reflexión:
Para verificar que obtenemos la dirección correcta, podemos usar
runtime.FuncForPC()
.Esto le da el mismo valor que su
funcPC()
función. Ver este ejemplo:Produce (pruébalo en Go Playground ):
¿Por qué? Debido a que el valor de una función en sí mismo es un puntero (solo tiene un tipo diferente que un puntero, pero el valor que almacena es un puntero) que debe desreferenciarse para obtener la dirección del código .
Entonces, lo que necesitaría para obtener esto
uintptr
(dirección de código) dentrofuncPC()
sería simplemente:Por supuesto que no se compila, reglas de conversión no permiten la conversión de un valor de función a
*uintptr
.Otro intento puede ser convertirlo primero a
unsafe.Pointer
, y luego a*uintptr
:Nuevamente: las reglas de conversión no permiten convertir valores de función a
unsafe.Pointer
. Cualquier tipo de puntero yuintptr
valores se pueden convertirunsafe.Pointer
y viceversa, pero no valores de función.Es por eso que tenemos que tener un valor de puntero para comenzar. ¿Y qué valor de puntero podríamos tener? Sí, la dirección de
f
:&f
. Pero este no será el valor de la función, esta es la dirección delf
parámetro (variable local). Entonces,&f
esquemáticamente no es (solo) un puntero, es un puntero a puntero (ambos deben ser desreferenciados). Todavía podemos convertirlo aunsafe.Pointer
(porque cualquier valor de puntero califica para eso), pero no es el valor de la función (como puntero), sino un puntero a él.Y necesitamos la dirección del código del valor de la función, por lo que tenemos que usar
**uintptr
para convertir elunsafe.Pointer
valor, y tenemos que usar 2 desreferencias para obtener la dirección (y no solo el punterof
).Esto es exactamente por qué
funcPC1()
da un resultado diferente, inesperado e incorrecto:Devuelve el puntero
f
, no la dirección del código real.fuente
conversion rules
acerca de no permitir la conversión defunction value
auintptr
.x
se puede convertir para escribirT
en cualquiera de estos casos ..."Devuelve un valor diferente porque ** (** uintptr) no es lo mismo que * (* uintptr). La primera es una doble indirección, la segunda una simple indirección.
En el primer caso, el valor es un puntero a un puntero a un puntero a un uint.
fuente