不可变字符串和指针地址

Immutable string and pointer address

在 Go 中 spec 写成:

Strings are immutable: once created, it is impossible to change the contents of a string.

我有以下代码:

str := "hello"
fmt.Printf("%p\n",&str) // 0x1040c128
fmt.Printf("%v\n",str) // hello
ptr := &str
*ptr = "world"
fmt.Printf("%p\n",&str) // 0x1040c128
fmt.Printf("%v\n",str) // world  

我原以为 &str 地址会在 *ptr = "world" 之后更改。就像 Java 一样,我们重新分配字符串引用。

这里的'immutability'是什么?

string 是不可变的。

str 不是 string 值。这是一个 变量 string 类型)。并且变量的值可能会更改,这是您对任何编程语言的期望。

"hello" 是一个 string 值,这是不可变的。 "world" 是另一个 string 值,当您将 "world" 分配给 str 时,您只是将另一个不同的值分配给 str 变量。无论您是直接对 str 执行此操作还是通过指针执行此操作都没有关系。您只是在更改 str.

表示的变量的值

不可变意味着您不能采用 string"world",并更改其第二个字符。例如,如果您有一个接收 string 参数的函数,无论它接收到什么(例如 "hello"),您都可以确保它始终保持不变。无论何时/如何打印此 string 值,它始终是 "hello".

A string 值是引擎盖下的结构值,由 reflect.StringHeader 类型表示:

type StringHeader struct {
    Data uintptr
    Len  int
}

它基本上存储了一个数据指针(指向保存文本的 UTF-8 编码值的字节数组)和 string 值的字节长度。数据数组及其长度不会向您公开,因此您无法修改它们。这是确保 string 值不可变的要素之一。另一个要素是,尽管 string 值可以被索引(索引其字节),但您不能为索引表达式分配新值。例如。使用值"abc"[0]是有效的,但是给它赋一个新值是无效的,比如"abc"[0] = 'x'。同样,您不能获取索引 string 值的索引表达式的地址(否则您可以修改指向的值,从而间接修改 string 值)。

这就是 language spec guarantees. Note that there are certain ways to still change string values, e.g. using package unsafe 的内容,但这不在规范的保证范围内:

Package unsafe contains operations that step around the type safety of Go programs.

Packages that import unsafe may be non-portable and are not protected by the Go 1 compatibility guidelines.

在您导入包 unsafe 的“那一刻”,您将失去语言规范提供的任何保证和安全,从那时起您就不能抱怨任何事情了。但是如果不使用这些“特殊”手段,string值就不会发生改变。

阅读博客 post Strings, bytes, runes and characters in Go 了解 string 如何在 Go 中实现和工作。

查看相关问题:

What are the possible consequences of using unsafe conversion from []byte to string in go?