Go的临时地址?

问题描述 投票:36回答:7

处理这种情况最简洁的方法是什么:

func a() string {
    /* doesn't matter */
}

b *string = &a()

这会生成错误:

不能取一个()的地址

我的理解是,如果采用地址,Go会自动将局部变量提升到堆中。这里很清楚,要采用返回值的地址。处理这个问题的惯用方法是什么?

pointers return go temporary rvalue
7个回答
36
投票

地址操作符返回指向具有“home”的东西的指针,例如,一个变量。代码中表达式的值是“无家可归者”。如果你真的需要一个*字符串,你必须分两步完成:

tmp := a(); b := &tmp

请注意,虽然* string的用例完全有效,但很多时候使用它们都是错误的。在Go中,string是一个值类型,但是传递一个便宜的(指针和int)。 String的值是不可变的,改变了“home”指向的*string更改,而不是字符串值,所以在大多数情况下根本不需要*string


14
投票

请参阅Go language spec的相关部分。 &只能用于:

  1. 可寻址的东西:变量,指针间接,切片索引操作,可寻址结构的字段选择器,可寻址数组的数组索引操作;要么
  2. 复合文字

你拥有的不是那些,所以它不起作用。

即使你能做到,我也不确定它意味着什么。取一个函数调用的结果地址?通常,您将某事物的指针传递给某人,因为您希望它们能够分配给指向的事物,并查看原始变量中的更改。但是函数调用的结果是暂时的;没有其他人“看到”它,除非你先把它分配给它。

如果创建指针的目的是创建具有动态生命周期的东西,类似于new()或获取复合文字的地址,那么您可以将函数调用的结果分配给变量并获取该地址。


5
投票

最后,您建议Go应该允许您获取任何表达式的地址,例如:

i,j := 1,2
var p *int = &(i+j)
println(*p)

当前的Go编译器打印错误:cannot take the address of i + j

在我看来,允许程序员获取任何表达式的地址:

  • 似乎没有用处(也就是说:它似乎在实际的Go程序中出现的概率非常小)。
  • 这会使编译器和语言规范复杂化。

将编译器和规范复杂化似乎适得其反。


1
投票

我最近被打成了类似的东西。

首先在你的例子中谈论字符串是一种分心,使用结构代替,重写它像:

func a() MyStruct {
    /* doesn't matter */
}

var b *MyStruct = &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的指针。出现了同样的复制问题。


0
投票

a()没有指向变量,因为它在堆栈上。你不能指向堆栈(为什么你呢?)。

如果你愿意,你可以这样做

va := a()
b := &va

但是你真正想要实现的目标有点不清楚。


0
投票

猜你需要更有效的C ++帮助;-)

Temp obj和rvalue

“C ++中的真正临时对象是不可见的 - 它们不会出现在您的源代码中。每当创建非堆对象但未命名时,它们就会出现。这些未命名的对象通常出现在以下两种情况之一中:当应用隐式类型转换以使函数调用成功时以及函数返回对象时。

来自Primer Plus

lvalue是一个数据对象,可以通过用户(命名对象)通过地址引用。非左值包括文字常量(除了引用的字符串,由其地址表示),具有多个术语的表达式,例如(a + b)。

在Go lang中,字符串文字将转换为StrucType对象,该对象将是一个不可寻址的临时结构对象。在这种情况下,Go中的地址不能引用字符串文字。

好吧,最后但并非最不重要的一个例外,你可以获取复合文字的地址。天哪,真是一团糟。


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用于取消引用指针并直接访问保存数据的内存区域(当然,在堆上)。

玩代码:https://play.golang.org/p/VDhycPwRjK9

© www.soinside.com 2019 - 2024. All rights reserved.