在接触Go这样的语言的时候,可能经常会听到这样一句话。您可能想知道字符串不能被修改。我们在日常开发中修改字符串是很正常的。为什么说Go中的字符串不可修改?本文将通过实际案例告诉你为什么Go中的字符串不能被修改。在演示这个问题之前,我们先对string类型的基础知识做一个大概的演示,让大家对这个问题有更进一步的理解。字符串定义字符串是一种用于表示字符的数据类型。使用时,用“”将字符内容括起来。例如下面的形式:packagemainimport"fmt"funcmain(){varstrstring="HelloWorld!"}在Go中,通常有三种定义字符串的方式://第一种(完整定义)var变量namestring="Stringcontent"//类型推导varvariablename="stringcontent"//短标记(只适用于局部变量)variablename:="stringcontent"字符串定义,其实也可以传bytes的方式。此处列出的方法是最常见的方法。字符串的组成Go中的字符串符合Unicode[1]标准,采用UTF-8[2]编码。字符串的底层实际上是由字节组成的(后面会详细解释)。使用下面的例子打印查看具体的字节内容:s:="HelloWorld!"for_,v:=ranges{fmt.Print(v)fmt.Print("\t")}//72101108108111328711111410810033上面代码打印出来的内容就是每个字符所代表的字节码。字符串不可修改通过上面粗略的演示,我们对字符串有了基本的了解。您可能想知道字符串不能被修改。我们在日常开发中重新赋值字符串是很正常的。为什么说Go中的字符串不可修改?其实这个说法在这里需要更正。修改字符串不等同于重新分配一个值。开发中常用的方法其实就是一个重赋值的概念。str:="HelloWorld!"//重新赋值str="HelloGo!"//修改字符串str[0]="I"一般不能修改,其实指的是上面代码的第二种方法。而且这样修改会报错::cannotassigntos[0](valueoftypebyte)回到正题,为什么Go中的字符串不能通过下标修改?这是因为Go中字符串的数据结构是由指针和长度组成的结构,指针指向的切片才是真正的字符串值。Go中的源码有这样的定义:typestringStructstruct{strunsafe.Pointer//pointertoasliceofbytetypelenint//thelengthofthestring}正是因为底层是[]的一片byte类型,我们使用下标的时候是用来修饰值的。这时候绝对不允许把一个字符内容赋值给byte类型。但是我们可以通过下标访问对应的字节值。fmt.Println(s[0])//output:72想通过下标修改值怎么办?这时候就需要通过切片来定义,然后转成字符串。packagemainimport("fmt")funcmain(){s1:=[]byte{72,101,108,108,111,32,87,111,114,108,100,33}fmt.Println(string(s1))//将“H”更改为ls1[0]=108fmt.Println(string(s1))}//输出:HelloWorld!世界你好!去assignment,回来回答日常开发中的assignment方法。packagemainimport("fmt")funcmain(){//声明一个字符串并给它一个初始值s:="HelloWorld!"//重新赋值变量ss:="HelloGo!"}那为什么这个场景下可以重新赋值字符串呢?这是因为,在Go的底层,其实是新建了一个[]byte{}类型的slice,变量s中的指针指向了新的内存空间地址(即HelloGohere!)。原来的你好世界!内存空间将通过垃圾回收机制回收。为什么这样的设计可能大家会考虑,为什么一个普通的字符串设计的那么复杂,还需要指针。暂时没有找到官方文档中的描述。1、我个人的猜测是当遇到很长的字符时,这使得字符串非常轻量级,可以轻松传递而不用担心内存复制。虽然在Go中不管是引用类型还是值类型参数传递都是值传递。但是指针显然比按值传递更节省内存。参考链接[1]Unicode:https://naveenr.net/unicode-character-set-and-utf-8-utf-16-utf-32-encoding/[2]UTF-8:https://naveenr.net/unicode-character-set-and-utf-8-utf-16-utf-32-encoding/
