处理这种情况的最干净的方法是什么:
func a() string {
/* doesn't matter */
}
b *string = &a()
这会产生错误:
不能取 a() 的地址
我的理解是,如果获取地址,Go 会自动将局部变量提升到堆中。这里很清楚,要取返回值的地址。处理这个问题的惯用方法是什么?
地址运算符返回指向具有“家”的东西的指针,例如变量。您的代码中表达式的值是“无家可归”。如果你真的需要一个 *string,你必须分两步完成:
tmp := a(); b := &tmp
请注意,虽然 *string 有完全有效的用例,但很多时候使用它们是错误的。在 Gostring
中是一种值类型,但传递起来很便宜(指针和 int)。字符串的值是不可变的,改变*string
“家”指向的变化,而不是字符串值,所以在大多数情况下*string
根本不需要。
请参阅Go 语言规范的相关部分。&
只能用于:
你所拥有的都不是那些,所以它不起作用。
即使你能做到,我什至不确定这意味着什么。获取函数调用结果的地址?通常,您将某事物的指针传递给某人,因为您希望他们能够分配给指向的事物,并查看原始变量的变化。但是函数调用的结果是暂时的;除非您先将其分配给某物,否则没有其他人“看到”它。
如果创建指针的目的是创建具有动态生命周期的东西,类似于new()
或获取复合文字的地址,那么您可以将函数调用的结果分配给变量并获取该变量的地址。
最后,您建议 Go 应该允许您获取任何表达式的地址,例如:
i,j := 1,2
var p *int = &(i+j)
println(*p)
当前的 Go 编译器打印错误:cannot take the address of i + j
在我看来,允许程序员获取任何表达式的地址:
使编译器和规范复杂化而收效甚微似乎适得其反。
我最近因类似的事情而陷入困境。
首先在您的示例中谈论字符串会分散注意力,请改用结构,将其重写为:
func a() MyStruct {
/* doesn't matter */
}
var b *MyStruct = &a()
这不会编译,因为您不能获取 a() 的地址。所以这样做:
func a() MyStruct {
/* doesn't matter */
}
tmpA := a()
var b *MyStruct = &tmpA
这将编译,但是您在堆栈上返回了一个 MyStruct,在堆上分配了足够的空间来存储一个 MyStruct,然后将内容从堆栈复制到堆。如果你想避免这种情况,那么这样写:
func a2() *MyStruct {
/* doesn't matter as long as MyStruct is created on the heap (e.g. use 'new') */
}
var a *MyStruct = a2()
复制通常很便宜,但这些结构可能很大。更糟糕的是,当您想修改结构并将其“粘贴”时,您不能复制然后修改副本。
无论如何,当您使用返回类型的 interface{} 时,它会变得更加有趣。interface{} 可以是结构体或指向结构体的指针。出现同样的复制问题。
分配给新变量时,您无法直接获取结果的引用,但是您可以通过简单地预先声明“b”指针来使用惯用的方法来执行此操作,而无需使用临时变量(这是无用的) - 这是您错过的真正步骤:
func a() string {
return "doesn't matter"
}
b := new(string) // b is a pointer to a blank string (the "zeroed" value)
*b = a() // b is now a pointer to the result of `a()`
*b
用于取消引用指针并直接访问保存数据的内存区域(当然是在堆上)。
玩代码: https: //play.golang.org/p/VDhycPwRjK9
a()
不指向变量,因为它在堆栈上。你不能指向堆栈(你为什么要?)。
如果你愿意,你可以这样做
va := a()
b := &va
但是您真正想要实现的目标还不清楚。
在撰写本文时,没有一个答案能真正解释为什么会这样。
考虑以下:
func main() {
m := map[int]int{}
val := 1
m[0] = val
v := &m[0] // won't compile, but let's assume it does
delete(m, 0)
fmt.Println(v)
}
如果这个代码片段真的编译了,会v
指向什么!?这是一个悬空指针,因为底层对象已被删除。
鉴于此,禁止寻址临时人员似乎是一个合理的限制
*string
是的,当 API 需要使用输入时,即使您经常希望将文字字符串传递给它们,这也会很烦人。
为此,我做了一个非常小的函数:
// Return pointer version of string
func p(s string) *string {
return &s
}
然后我没有尝试调用foo("hi")
并获得可怕的东西cannot use "hi" (type string) as type *string in argument to foo
,而是将参数包装在对 to 的调用中p()
:
foo(p("hi"))
猜你需要更有效的 Cpp的帮助;-)
临时对象和右值
“<strong>C++ 中真正的临时对象是不可见的——它们不会出现在您的源代码中。每当创建了非堆对象但未命名时,它们就会出现。此类未命名对象通常出现在以下两种情况之一:应用隐式类型转换以使函数调用成功时以及函数返回对象时。”</p>
从Primer Plus
lvalue是一个数据对象,可以通过用户(命名对象)按地址引用。非左值包括文字常量(除了引用的字符串,它们由它们的地址表示),具有多个术语的表达式,例如 (a + b)。
在 Go 语言中,字符串文字将被转换为StrucType
对象,这将是一个不可寻址的临时结构对象。在这种情况下,字符串文字不能被 Go 中的地址引用。
好吧,最后但并非最不重要的一个例外是,您可以获取复合文字的地址。天哪,真是一团糟。