如何获取函数调用的返回值的指针?

huangapple go评论81阅读模式
英文:

How to get the pointer of return value from function call?

问题

我只需要一个指向time.Time的指针,所以下面的代码似乎是无效的:

> ./c.go:5: 无法取time.Now()的地址

我想知道为什么会这样?除了先将其赋值给一个变量然后再取变量的指针之外,还有其他方法吗?

package main

import "time"
func main() {
    _ = &time.Now()
}
英文:

I just need a pointer to time.Time, so the code below seems invalid:

> ./c.go:5: cannot take the address of time.Now()

I just wonder why? Is there any way to do that except to do assignment to a variable first and take the pointer of the variable?

package main

import "time"
func main() {
    _ = &time.Now()
}

答案1

得分: 77

可能令人不满意的答案是“你不能这样做,因为规范是这样规定的。”规范规定,要在某个东西上使用&,它必须是可寻址的或者是一个复合字面量,并且要可寻址,它必须是“一个变量、指针间接引用或切片索引操作;或者是一个可寻址结构操作数的字段选择器;或者是一个可寻址数组的数组索引操作。”函数调用和方法调用明显不在列表中。

从实际角度来看,这可能是因为函数的返回值可能没有可用的地址;它可能在寄存器中(在这种情况下肯定不可寻址)或者在堆栈中(在这种情况下它有一个地址,但如果放在逃逸当前作用域的指针中,该地址将无效)。为了保证可寻址性,Go 语言必须几乎完全等同于将其赋值给一个变量。但是 Go 是一种语言,它认为如果要为一个变量分配存储空间,那肯定是因为你说要这样做,而不是因为编译器神奇地决定要这样做。所以它不会使函数的结果可寻址。

或者我可能想得太多了,他们只是不想为返回一个值的函数和返回多个值的函数设置特殊情况 如何获取函数调用的返回值的指针?

英文:

The probably unsatisfying answer is "you can't do it because the spec says so." The spec says that to use & on something it has to be addressable or a compound literal, and to be addressable it has to be "a variable, pointer indirection, or slice indexing operation; or a a field selector of an addressable struct operand; or an array indexing operation of an addressable array." Function calls and method calls are definitely not on the list.

Practically speaking, it's probably because the return value of a function may not have a usable address; it may be in a register (in which case it's definitely not addressable) or on the stack (in which case it has an address, but one that won't be valid if it's put in a pointer that escapes the current scope. To guarantee addressability, Go would have to do pretty much the exact equivalent of assigning it to a variable. But Go is the kind of language that figures that if it's going to allocate storage for a variable it's going to be because you said to, not because the compiler magically decided to. So it doesn't make the result of a function addressable.

Or I could be over-thinking it and they simply didn't want to have a special case for functions that return one value versus functions that return multiple 如何获取函数调用的返回值的指针?

答案2

得分: 42

你不能直接获取函数调用的地址(或者更准确地说是函数的返回值),正如hobbs所描述的那样。

还有一种方法,但是比较丑陋:

p := &[]time.Time{time.Now()}[0]
fmt.Printf("%T %p\n%v", p, p, *p)

输出结果(Go Playground):

*time.Time 0x10438180
2009-11-10 23:00:00 +0000 UTC

这里发生的情况是创建了一个带有字面值的结构体,其中包含一个元素(time.Now()的返回值),然后对切片进行索引(取第0个元素),并获取第0个元素的地址。

因此,最好使用一个局部变量:

t := time.Now()
p := &t

或者使用一个辅助函数:

func ptr(t time.Time) *time.Time {
    return &t
}

p := ptr(time.Now())

这也可以是一个一行的匿名函数:

p := func() *time.Time { t := time.Now(); return &t }()

或者作为另一种选择:

p := func(t time.Time) *time.Time { return &t }(time.Now())

有关更多替代方法,请参见:

https://stackoverflow.com/questions/30716354/how-do-i-do-a-literal-int64-in-go

还可以参考相关问题:https://stackoverflow.com/questions/34197248/how-can-i-store-reference-to-the-result-of-an-operation-in-go/34197367#34197367

英文:

You can't directly take the address of a function call (or more precisely the return value(s) of the function) as described by hobbs.

There is another way but it is ugly:

p := &[]time.Time{time.Now()}[0]
fmt.Printf("%T %p\n%v", p, p, *p)

Output (Go Playground):

*time.Time 0x10438180
2009-11-10 23:00:00 +0000 UTC

What happens here is a struct is created with a literal, containing one element (the return value of time.Now()), the slice is indexed (0<sup>th</sup> element) and the address of the 0<sup>th</sup> element is taken.

So rather just use a local variable:

t := time.Now()
p := &amp;t

Or a helper function:

func ptr(t time.Time) *time.Time {
	return &amp;t
}

p := ptr(time.Now())

Which can also be a one-liner anonymous function:

p := func() *time.Time { t := time.Now(); return &amp;t }()

Or as an alternative:

p := func(t time.Time) *time.Time { return &amp;t }(time.Now())

For even more alternatives, see:

https://stackoverflow.com/questions/30716354/how-do-i-do-a-literal-int64-in-go

Also see related question: https://stackoverflow.com/questions/34197248/how-can-i-store-reference-to-the-result-of-an-operation-in-go/34197367#34197367

答案3

得分: 7

幸运的是,泛型现在提供了一个相当干净的解决方案,只需定义一个函数一次,就可以在任何类型上使用:

package main

func ptr[T any](x T) *T {
	return &x
}

func main() {
	print(ptr("foo"))
	print(ptr(42))
}

Playground: https://go.dev/play/p/TgpEPKjpXX7

然而,这只适用于 Golang 1.18 及以上的版本。对于之前的版本,你需要为每种类型编写一个函数,就像其他答案建议的那样。

英文:

Fortunately, generics now offer quite a clean solution by defining a function only one time, that can be used on any type:

package main

func ptr[T any](x T) *T {
	return &amp;x
}

func main() {
	print(ptr(&quot;foo&quot;))
	print(ptr(42))
}

Playground: https://go.dev/play/p/TgpEPKjpXX7

However, this will work only starting from Golang 1.18. For previous versions, you'll need a function for each type, as other answers suggested.

答案4

得分: -1

如果您在使用自己编写的函数时遇到了这个问题,请将您的函数更改为返回指针。尽管您无法获取返回值的地址,但您可以对返回值进行解引用,因此无论您想要指针还是对象,它都是合适的。

func Add(x, y int) *int {
    tmp := x + y
    return &tmp
}

func main() {
    fmt.Println("我想要指针:", Add(3, 4))
    fmt.Println("我想要对象:", *Add(3, 4))
}

https://play.golang.org/p/RogRZDNGdmY

英文:

If you are having this trouble with a function you wrote, change your function to return a pointer. Even though you can't take the address of a return value, you can dereference a return value, so it will be suitable whether you want the pointer or the object.

func Add(x, y int) *int {
    tmp := x + y
    return &amp;tmp
}

func main() {
    fmt.Println(&quot;I want the pointer: &quot;, Add(3, 4))
    fmt.Println(&quot;I want the object: &quot;, *Add(3, 4))
}

https://play.golang.org/p/RogRZDNGdmY

huangapple
  • 本文由 发表于 2015年6月10日 08:07:03
  • 转载请务必保留本文链接:https://go.coder-hub.com/30744965.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定