如何以最简洁的方式处理这种情况:
func a() string {
/* doesn't matter */
}
b *string = &a()
这会生成错误:
无法获取a()的地址
我的理解是,如果取一个局部变量的地址,Go会自动将其提升到堆上。在这里,很明显需要获取返回值的地址。有什么惯用的方法来处理这个问题呢?
地址运算符返回指向具有“家”的某个东西的指针,例如变量。你代码中表达式的值是“无家可归的”。如果你确实需要一个*字符串,你需要分两步完成:
tmp := a(); b := &tmp
请注意,虽然有完全有效的使用*string的情况,但很多时候使用它是错误的。在Go中,string
是一个值类型,但是一个很便宜的类型以便于传递(一个指针和一个整数)。字符串的值是不变的,改变一个*string
会改变“home”所指向的位置,而不是字符串值,因此在大多数情况下根本不需要*string
。
a()
返回一个*string
。 - thwd&
运算符获取地址的复合字面量有一个“家”(无论如何都没有其他选择,否则就没有地址可取)。 - zzzz&
仅适用于以下情况:
&
。new()
或获取复合字面量的地址,则可以将函数调用的结果分配给一个变量,然后取该变量的地址。i,j := 1,2
var p *int = &(i+j)
println(*p)
当前的 Go 编译器会输出错误信息:无法获取 i + j 的地址
我认为,允许程序员获取任何表达式的地址:
为了获得微小的收益而使编译器和规范变得复杂似乎是适得其反的。
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
用于解引用指针并直接访问保存数据的内存区域(当然是在堆上)。
当API要求使用* string
输入时,即使您经常想将文字字符串传递给它们,这可能会很烦人。
为此,我编写了一个非常简小的函数:
// 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
,我只需将参数包装在对p()
的调用中:
foo(p("hi"))
最近我也遇到了类似的问题。
首先,在你的例子中谈论字符串是一个干扰,应该使用结构体进行重写,例如:
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()
复制通常不会花费太多,但这些结构可能很大。更糟糕的是,当你想修改结构并使其保持一致时,你不能复制然后修改副本。
无论如何,当您使用接口 {} 的返回类型时,它变得更加有趣。接口 {} 可以是该结构体或指向结构体的指针。同样的复制问题也会出现。
a()
不指向堆栈上的变量。你不能指向堆栈(为什么要这样做?)。
如果你想的话,可以这样做
va := a()
b := &va
va
将被存储在堆上。 - Matt Joinerva
将存储在堆上吗? - Matt Joiner
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
会指向什么?!它是一个悬空指针,因为底层对象已被删除。猜想你需要来自更有效的Cpp的帮助 ;-)
临时对象和rvalue
“在C++中,真正的临时对象是看不见的-它们不会出现在您的源代码中。它们在创建非堆对象但未命名时产生。这些未命名的对象通常出现在两种情况下:当应用隐式类型转换以使函数调用成功时,以及当函数返回对象时。”
来自Primer Plus的内容
lvalue是可以通过用户(命名对象)地址引用的数据对象。非lvalues包括文字常量(除了引用其地址的引号字符串),具有多个术语的表达式,例如(a + b)。
在Go语言中,字符串字面值将转换为StrucType
对象,这将是一个不可寻址的临时结构对象。在这种情况下,在Go中无法通过地址引用字符串字面值。
最后但并非最不重要的一点,在go中有一个例外,您可以获取组合文字的地址。天哪,这太混乱了。