String 对Go中字符串文字的引用

String 对Go中字符串文字的引用,string,pointers,go,string-literals,String,Pointers,Go,String Literals,在我的应用程序中,我经常传递对静态字符串的引用。我希望避免为每个调用分配内存,但我无法获取字符串文本的地址 为什么不能获取字符串文本的地址(请参见下面示例中的test1())?我是否误解了语法,或者这是由于Go的内部工作造成的限制 如果不可能,最好的解决方案是什么 test2()可以工作,但是每次它都会为var-hej分配内存吗? test3()不会分配任何新内存,但我希望避免函数之外的混乱 package main import "fmt" var konnichiwa = `こんにちは世

在我的应用程序中,我经常传递对静态字符串的引用。我希望避免为每个调用分配内存,但我无法获取字符串文本的地址

为什么不能获取字符串文本的地址(请参见下面示例中的
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())
}

谢谢你的帮助

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

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


只需传递字符串。

获取文本(字符串、数字等)的地址是非法的,因为它的语义不明确

你是用实际常数的地址吗?这将允许修改该值(并可能导致运行时错误),还是要分配一个新对象,复制常量并将地址获取到新版本

test2
的情况下不存在这种歧义,因为您处理的是一个语义定义明确的现有变量。如果字符串被定义为
const
,则相同的操作将不起作用

语言规范通过明确不允许您要求的内容来避免这种歧义。解决方案是
test2
。虽然它略为冗长,但它保持了规则的简单和干净

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

func f() interface{} {
    return &struct {
        A int
        B int
    }{1, 2} 
}
  • 传递字符串通常不会在Go中分配内存—它是一种值类型(ptr表示字节和int len)

  • 只有像这样的复合文字才支持采用文字的地址

    v:=&T{1,“foo”}

    但不适用于简单的值,如

    w:=&1

    x:=&“foo”


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

  • 传递字符串类型而不是*字符串
  • 不要对幕后发生的事情做出假设
  • 给出建议“不要担心分配字符串”是很有诱惑力的,因为在您描述相同字符串的传递位置时,这实际上是正确的,可能是多次。虽然这是一般性的,但考虑一下内存的使用真的很好。猜测真的很糟糕,根据另一种语言的经验来猜测更糟糕

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

    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的调用。只有一个!我会为你破坏它,它在测试1中


    因此,在不偏离太多切线的情况下,对编译器输出的一点观察表明,我们通过使用字符串类型而不是*string来避免分配。

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

    testCases := []struct {
            Values map[string]interface{}
        }{
            {
                Values: map[string]interface{}{"var1": nil},
            },
            {
                Values: map[string]interface{}{"var1": "my_cool_string"},
            },
        }
    
    稍后在以下代码中(您可能还需要断言检查):


    我不仅更好地理解了Go何时决定分配新内存,而且更好(或更糟!)您还教我如何使用
    Go工具
    自己获取程序集。该死的,现在我将坐在那里分析Go的汇编代码,享受几个小时的乐趣。谢谢(是的,我会坚持你的解决方案)我得到了错误
    go工具:使用go 1.10时没有这样的工具“6g”
    ,但这是有效的:
    go工具编译-S t.go
    对于空字符串为有效值的结构,需要什么可选属性?这没有帮助,因为某些API需要向字符串传递指针。
    if v != nil {
        vv := v.(string)
    }