在Go中引用字符串文字

ANi*_*sus 16 string pointers go string-literals

在我的应用程序中,我将经常传递对静态字符串的引用.我希望避免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())
}
Run Code Online (Sandbox Code Playgroud)

感谢帮助!

Son*_*nia 13

对于传递"静态"字符串的情况的最佳解决方案的问题,

  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())
}
Run Code Online (Sandbox Code Playgroud)

现在问编译器:

> go tool 6g -S t.go
Run Code Online (Sandbox Code Playgroud)

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

因此,如果没有太多的切线,那么对编译器输出的一点看就表明我们通过使用字符串类型而不是*string来避免分配.

  • 当Go真正决定分配新内存时,我不仅能更好地理解,而且更好(或者更糟!)你还教我如何使用`go tool`来自己获得程序集.诅咒,现在我将坐在分析Go的汇编代码几个小时的乐趣.谢谢!(是的,我会坚持你的解决方案) (4认同)

jim*_*imt 10

获取文字的地址(字符串,数字等)是非法的,因为它具有模糊的语义.

你拿的是实际常数的地址吗?哪个允许修改值(并可能导致运行时错误)或者您是否要分配新对象,复制常量并将地址转换为新版本?

test2由于您正在处理明确定义语义的现有变量,因此不存在这种歧义.如果将字符串定义为,则同样不起作用const.

语言规范通过明确地不允许您要求的内容来避免这种模糊性.解决方案是test2.虽然它稍微冗长一点,但它使规则简单而干净.

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

func f() interface{} {
    return &struct {
        A int
        B int
    }{1, 2} 
}
Run Code Online (Sandbox Code Playgroud)


Den*_*ret 7

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

因此,您不必使用指针来有效地处理它.

只需传递字符串.

  • 这没有帮助,因为某些 API 需要传递指向字符串的指针。 (4认同)

zzz*_*zzz 5

  1. 在 Go 中传递字符串通常不会分配内存 - 它是一种值类型(指向字节的 ptr 和 int len)。

  2. 仅支持复合文字,例如

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

    但不适用于像这样的简单值

    w := &1

    x := &"foo"