23

在我的应用程序中,我会经常传递对静态字符串的引用。我希望避免让 Go 为每个调用分配内存,但我未能将地址获取到我的字符串文字。

为什么不能获取字符串文字的地址(参见test1()下面的示例)?我是否误解了语法,还是由于 Go 的内部工作原理而受到限制?

如果不可能,最好的解决方案是什么?

test2()有效,但它会为var hej每次分配内存吗?
test3()不会分配任何新内存,但我希望避免函数之外的混乱。

package main

import "fmt"

var konnichiwa = `こんにちは世界`

// Gives the compile error `cannot take the address of "Hello world"`
func test1() (*string) { return &`Hello world` }

// Works fine
func test2() (*string) {
    hej := `Hej världen`
    return &hej
}

func test3() (*string) { return &konnichiwa }

func main(){
    fmt.Println(*test1())
    fmt.Println(*test2())
    fmt.Println(*test3())
}

感谢帮助!

4

5 回答 5

18

获取文字(字符串、数字等)的地址是非法的,因为它具有模棱两可的语义。

您是否正在获取实际常量的地址?哪个允许修改值(并可能导致运行时错误)或者您是否要分配一个新对象、复制常量并获取新版本的地址?

在这种情况下不存在这种歧义,test2因为您正在处理语义明确定义的现有变量。如果字符串被定义为const.

语言规范通过明确不允许您要求的内容来避免这种歧义。解决办法是test2。虽然它稍微有点冗长,但它使规则保持简单和干净。

当然,每条规则都有其例外,在 Go 中,这涉及合成文字:以下是合法的,并在规范中定义为:

func f() interface{} {
    return &struct {
        A int
        B int
    }{1, 2} 
}
于 2012-06-18T19:03:01.807 回答
17

对于传递“静态”字符串的最佳解决方案的问题,

  1. 传递字符串类型而不是 *string。
  2. 不要对幕后发生的事情做出假设。

给出“不要担心分配字符串”的建议很有诱惑力,因为在您描述相同字符串在哪里传递的情况下,这实际上是正确的,也许是很多次。不过一般来说,考虑内存使用真的很好。猜测真的很糟糕,更糟糕的是根据使用另一种语言的经验进行猜测。

这是您的程序的修改版本。你猜内存是在哪里分配的?

package main

import "fmt"

var konnichiwa = `こんにちは世界`

func test1() *string {
    s := `Hello world`
    return &s
}

func test2() string {
    return `Hej världen`
}

func test3() string {
    return konnichiwa
}

func main() {
    fmt.Println(*test1())
    fmt.Println(test2())
    fmt.Println(test3())
}

现在询问编译器:

> go tool 6g -S t.go

(我将程序命名为 t.go。)在输出中搜索对 runtime.new 的调用。只有一个!我会为你破坏它,它在 test1 中。

因此,在没有过多切线的情况下,稍微看一下编译器输出就表明我们通过使用字符串类型而不是 *string 来避免分配。

于 2012-06-18T19:44:36.817 回答
7

go 中的字符串是不可变的,只是一个指针和一个长度(总长度:2 个字)。

所以你不必使用指针来有效地处理它。

只需传递字符串。

于 2012-06-18T19:02:08.083 回答
3
  1. 传递一个字符串通常不会在 Go 中分配内存——它是一个值类型(ptr 到字节和一个 int len)。

  2. 只有复合文字才支持获取文字的地址,例如

    v := &T{1, "foo"}

    但不适用于简单的值,例如

    w := &1

    x := &"foo"

于 2012-06-18T19:07:42.417 回答
0

我使用 interface{} 表示我的字符串有时为零。就我而言,它看起来像:

testCases := []struct {
        Values map[string]interface{}
    }{
        {
            Values: map[string]interface{}{"var1": nil},
        },
        {
            Values: map[string]interface{}{"var1": "my_cool_string"},
        },
    }

稍后在以下代码中(您可能还需要断言检查):

if v != nil {
    vv := v.(string)
}
于 2020-03-20T08:53:50.113 回答