一尘不染

不变的字符串和指针地址

go

在Go 规范中写道:

字符串是不可变的:一旦创建,就无法更改字符串的内容。

我有以下代码:

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那样,我们在其中重新分配String引用。

什么是“不变性”?


阅读 248

收藏
2020-07-02

共1个答案

一尘不染

string 是不可变的。

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

"hello"是一个string值,这是不可变的。"world"是另一个string值,当您分配"world"给时str,您只需为str变量分配另一个不同的值。是直接对str还是通过指针进行都没关系。您只是在更改由表示的变量的值str

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

string值是在引擎盖下一个struct值,由表示的reflect.StringHeader类型:

type StringHeader struct {
    Data uintptr
    Len  int
}

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

这就是语言规范所保证的。请注意,有些方法仍然可以更改string值,例如使用package
unsafe,但这超出了规范的保证范围:

不安全的软件包包含绕过Go程序的类型安全的操作。

导入不安全的软件包可能是不可移植的,并且不受Go 1兼容性准则的保护。

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

阅读博客文章Go中的字符串,字节,符文和字符,了解如何在Gostring中实现和工作。

2020-07-02