Go中对字符串字面值的引用

huangapple go评论77阅读模式
英文:

Reference to string literals in Go

问题

在我的应用程序中,我经常会传递对静态字符串的引用。我希望避免每次调用时Go为其分配内存,但是我无法获取到字符串字面量的地址。

为什么无法获取字符串字面量的地址(请参见下面示例中的test1())?我是否误解了语法,还是这是由于Go的内部工作机制的限制?

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

test2()可以工作,但是它会为var hej每次分配内存吗?
test3()不会分配任何新的内存,但是我希望避免在函数外部添加杂乱的代码。

谢谢帮助!

英文:

In my application I will frequently pass references to a static string. I wish to avoid having Go allocate memory for each call, but I failed to get the address to my string literal.

Why is it not possible to take the address of a string literal (see test1() in the example below)? Have I misunderstood the syntax, or is it a limitation due to the internal workings of Go?

If not possible, what would be the best solution?

test2() works, but will it allocate memory for the var hej each time?
test3() will not allocate any new memory, but I wish to avoid clutter outside the function.

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

Thanks for help!

答案1

得分: 22

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

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

test2的情况下,不存在这种歧义,因为您正在处理一个已经存在的变量,其语义是明确定义的。如果字符串被定义为const,则同样的方法将不起作用。

语言规范通过明确禁止您所要求的内容来避免这种歧义。解决方案是test2。虽然它稍微冗长一些,但它保持了规则的简单和清晰。

当然,每个规则都有例外,在Go语言中,这涉及到复合字面量:以下是合法的,并在规范中明确定义:

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

Taking the address of a literal (string, number, etc) is illegal because it has ambiguous semantics.

Are you taking the address of the actual constant? Which would allow the value to be modified (and could lead to a runtime error) or do you want to allocate a new object, copy the constant over and get the address to the new version?

This ambiguity does not exist in the case of test2 since you are dealing with an existing variable of which the semantics are clearly defined. The same would not work if the string was defined as const.

The language spec avoids this ambiguity by explicitly not allowing what you are asking for. The solution is test2. While it is slightly more verbose, it keeps the rules simple and clean.

Of course, every rule has its exceptions, and in Go this concerns composit literals: The following is legal and defined as such in the spec:

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

答案2

得分: 19

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

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

英文:

For the question of the best solution for your situation of passing around "static" strings,

  1. Pass the string type instead of *string.
  2. Don't make assumptions about what is going on behind the scenes.

It's tempting to give the advice "don't worry about allocating strings" because that's actually true in the case you're describing where the same string is passed around, perhaps many times. It general though, it's really good to think about memory use. It's just really bad to guess, and even worse to guess based on experience with another language.

Here's a modified version of your program. Where do you guess that memory is allocated?

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

Now ask the compiler:

> go tool 6g -S t.go

(I named the program t.go.) Search the output for calls to runtime.new. There's only one! I'll spoil it for you, it's in test1.

So without going off on too much of a tangent, the little look at the compiler output indicates that we avoid allocation by working with the string type rather than *string.

答案3

得分: 7

在Go语言中,字符串是不可变的,它只是一个指针和一个长度(总长度为2个字)。因此,你不需要使用指针来高效地处理它。只需传递字符串即可。

英文:

A string in go is immutable and is just a pointer and a length (total length : 2 words).

So you don't have to use a pointer to handle it efficiently.

Just pass the string.

答案4

得分: 5

  1. 在Go语言中,传递字符串通常不会分配内存 - 它是一个值类型(指向字节的指针和一个int类型的长度)。

  2. 只有在复合字面量(如 v := &T{1, "foo"})中才支持获取字面量的地址,而对于简单的值(如 w := &1x := &"foo")不支持。

英文:
  1. Passing around a string doesn't usually allocate memory in Go - it's a value type (ptr to the bytes and an int len).

  2. Taking an address of a literal is supported only for composite literals like

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

but not for simple values like

w := &1

x := &"foo"

答案5

得分: 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)
}
英文:

I am using interface{} to indicate that my string will be sometimes nil. In my case it looks like:

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

And later in following code (You may also want the assertion check):

if v != nil {
	vv := v.(string)
}

huangapple
  • 本文由 发表于 2012年6月19日 02:38:39
  • 转载请务必保留本文链接:https://go.coder-hub.com/11088967.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定