48

处理这种情况的最干净的方法是什么:

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

b *string = &a()

这会产生错误:

不能取 a() 的地址

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

4

9 回答 9

52

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

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

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

于 2012-05-10T14:23:52.217 回答
15

请参阅Go 语言规范的相关部分。&只能用于:

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

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

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

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

于 2012-05-10T22:48:48.203 回答
5

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

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

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

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

  • 似乎不是很有用(即:在实际 Go 程序中出现的概率似乎很小)。
  • 这会使编译器和语言规范复杂化。

使编译器和规范复杂化而收效甚微似乎适得其反。

于 2012-05-11T11:18:57.940 回答
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{} 时,它会变得更加有趣。interface{} 可以是结构体或指向结构体的指针。出现同样的复制问题。

于 2012-05-11T12:25:21.190 回答
1

分配给新变量时,您无法直接获取结果的引用,但是您可以通过简单地预先声明“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

于 2019-05-05T10:45:21.007 回答
0

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

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

va := a()
b := &va

但是您真正想要实现的目标还不清楚。

于 2012-05-10T14:23:36.710 回答
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指向什么!?这是一个悬空指针,因为底层对象已被删除。

鉴于此,禁止寻址临时人员似乎是一个合理的限制

于 2020-12-15T16:13:39.220 回答
0

*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"))
于 2021-12-13T02:32:06.300 回答
-1

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

临时对象和右值

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

Primer Plus

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

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

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

于 2019-04-18T16:36:56.333 回答