在Go中引用字符串文字

时间:2012-06-18 18:38:40

标签: 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())
}

感谢您的帮助!

5 个答案:

答案 0 :(得分:12)

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

  1. 传递字符串类型而不是* string。
  2. 不要对幕后发生的事情做出假设。
  3. 提供建议“不要担心分配字符串”是很诱人的,因为在你描述传递相同字符串的位置时,这实际上是正确的,可能很多次。一般来说,考虑内存使用真的很好。这是非常糟糕的猜测,更糟糕的是根据另一种语言的经验猜测。

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

    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来避免分配。

答案 1 :(得分:9)

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

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

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

语言规范通过明确禁止您要求的内容来避免这种歧义。解决方案是test2。虽然它稍微冗长一点,但它使规则简单而干净。

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

func f() interface{} {
    return &struct {
        A int
        B int
    }{1, 2} 
}

答案 2 :(得分:6)

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

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

只需传递字符串。

答案 3 :(得分:2)

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

  2. 仅支持复合文字(如

    )获取文字地址

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

    但不适用于像

    这样的简单值

    w := &1

    x := &"foo"

答案 4 :(得分:0)

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

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)
}