处理此类案件的最简洁方法是什么:
func a() string {
/* doesn't matter */
}
b *string = &a()
这会产生错误:
不能取一个()
的地址
我的理解是,如果采用地址,Go会自动将局部变量提升到堆中。这里很清楚,要采用返回值的地址。处理这个问题的惯用方法是什么?
答案 0 :(得分:36)
地址运算符返回指向具有“home”的东西的指针,例如一个变量。代码中表达式的值是“无家可归者”。如果你真的需要*字符串,你必须分两步完成:
tmp := a(); b := &tmp
请注意,虽然* string的用例完全有效,但很多时候使用它们都是错误的。在Go string
中是一个值类型,但是传递一个便宜的值(指针和int)。字符串的值是不可变的,更改“home”指向的*string
更改,而不是字符串值,因此在大多数情况下根本不需要*string
。
答案 1 :(得分:14)
请参阅Go language spec的相关部分。 &
只能用于:
你拥有的不是那些,所以它不起作用。
即使你能做到,我也不确定它意味着什么。取一个函数调用的结果地址?通常,您将某事物的指针传递给某人,因为您希望它们能够分配给指向的事物,并查看原始变量中的更改。但是函数调用的结果是暂时的;没有其他人“看到”它,除非你先把它分配给某些东西。
如果创建指针的目的是创建具有动态生命周期的东西,类似于new()
或获取复合文字的地址,那么您可以将函数调用的结果赋给变量并取地址。
答案 2 :(得分:5)
最后,您建议Go应该允许您获取任何表达式的地址,例如:
i,j := 1,2
var p *int = &(i+j)
println(*p)
当前的Go编译器输出错误:cannot take the address of i + j
在我看来,允许程序员获取任何表达式的地址:
使编译器和规范复杂化似乎适得其反。
答案 3 :(得分:1)
我最近被打成了类似的东西。
首先在你的例子中谈论字符串是一种分心,使用结构代替,重写它像:
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 {}可以是struct或指向struct的指针。出现了同样的复制问题。
答案 4 :(得分:0)
a()
没有指向变量,因为它在堆栈上。你不能指向堆栈(为什么你呢?)。
如果你想要
,你可以这样做va := a()
b := &va
但你真正想要达到的目标有点不清楚。
答案 5 :(得分:0)
猜测您需要更有效的Cpp ;-)
的帮助临时obj和右值
在C ++中,“ 真正的临时对象”是不可见的-它们不会出现在您的源代码中。每当创建但未命名非堆对象时,它们就会出现。这种未命名的对象通常在以下两种情况之一中出现:应用隐式类型转换以使函数调用成功时以及函数返回对象时。”
并且来自 Primer Plus
左值是一个数据对象,可以通过用户通过地址(命名对象)进行引用。非左值包括文字常量(除了带引号的字符串,它们由它们的地址表示),带有多个术语的表达式,例如(a + b)。
在Go lang中,字符串文字将转换为StrucType
对象,该对象将是不可寻址的temp结构对象。在这种情况下,无法通过Go中的地址引用字符串文字。
最后一个但并非最不重要的一个例外是,您可以获取复合文字的地址。天哪,真是一团糟。
答案 6 :(得分:0)
在分配给新变量时,无法直接获取结果的引用,但是您可以通过简单地预先声明“ 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
用于取消引用指针,并直接访问保存数据的内存区域(当然是在堆上)。
答案 7 :(得分:0)
在撰写本文时,没有一个答案能真正解释为什么会这样。
请考虑以下内容:
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
会指向什么!这是一个悬空指针,因为基础对象已被删除。
鉴于此,似乎是合理的限制,禁止使用临时工