关于go:为什么说Go的字符串类型不能修改

2次阅读

共计 1902 个字符,预计需要花费 5 分钟才能阅读完成。

在接触 Go 这么语言,可能你常常会听到这样一句话。对于字符串不能批改,可能你很纳闷,日常开发中咱们对字符串进行批改也是很失常的,为什么又说 Go 中的字符串不能进行批改呢?

本文就来通过理论案例给大家演示,为什么 Go 中的字符串不能进行批改。

在演示这个问题之前,咱们先对字符串类型的基础知识做个大抵的演示,这样便于大家对问题的进一步理解。

本文已收录 Gitee、Github。分享 Go、PHP、MySQL、Redis 等等技术干货。举荐拜访 Gitee,速度快并且可能失常解析文档中的图片文件。

字符串定义

字符串是一种用来示意字符的数据类型。在应用时,应用 ” “ 将字符内容蕴含起来。例如上面的模式:

package main

import "fmt"

func main() {var str string = "Hello World!"}

在 Go 中,字符串通常有三种定义形式:

// 第一种(全量定义)
var 变量名称 string = "字符串内容"
// 类型推导
var 变量名称 = "字符串内容"
// 短标记(只实用于局部变量)
变量名称 := "字符串内容"

字符串的定义,其实也能够通过字节的形式。这里列举的形式是最为常见的形式。

字符串的组成

Go 中的字符串合乎 Unicode 规范,并且采纳 UTF- 8 编码。字符串底层其实也是由 byte 组成(前面会认真解说)。通过上面的示例,打印查看具体的字节内容:

s := "Hello World!"
for _, v := range s {fmt.Print(v)
    fmt.Print("\t")
}
// 72 101 108 108 111 32 87 111 114 108 100 33

下面代码打印的内容,就是每一个字符所示意的字节码。

字符串不能批改

通过下面的大抵演示,咱们对字符串有一个根本的理解。对于字符串不能批改,可能你很纳闷,日常开发中咱们对字符串进行从新赋值也是很失常的,为什么又说 Go 中的字符串不能进行批改呢?

其实这里要纠正这个谈话,对于字符串批改并不等价于从新赋值。开发中罕用的形式,其实是一种从新赋值的概念。

str := "Hello World!"
// 从新赋值
str = "Hello Go!"
// 字符串批改
str[0] = "I"

通常听到的不能批改,其实就是指的下面代码的第二种形式。并且通过这种形式批改会报错::cannot assign to s[0] (value of type byte)

回归正题,为什么 Go 中的字符串不能通过下标的形式来进行批改呢?
这是因为Go 中的字符串的数据结构体是由一个指针和长度组成的构造体,该指针指向的一个切片才是真正的字符串值。Go 中源码有这样一段定义:

type stringStruct struct {
    str unsafe.Pointer // 指向一个 byte 类型的切片指针
    len int // 字符串的长度
}

正是因为底层是一个[]byte 类型的切片,当咱们应用下标的形式去批改值,这时候将一个字符内容赋值给 byte 类型,必定是不容许的。然而咱们能够通过下标的形式去拜访对应的 byte 值。

fmt.Println(s[0]) // output:72

那咱们要想通过下标的形式去批改值该怎么办呢?这时候,就须要通过切片的形式来定义,而后在转成字符串。

package main

import ("fmt")

func main() {s1 := []byte{72, 101, 108, 108, 111, 32, 87, 111, 114, 108, 100, 33}
    fmt.Println(string(s1))
    // 将 "H" 批改为 l
    s1[0] = 108
    fmt.Println(string(s1))
}
// output:
Hello World!
lello World!

字符串的赋值

下面剖析了为什么字符串不能应用下标去赋值,回过来解答一下日常开发中的赋值形式。

package main

import ("fmt")

func main() {
    // 申明一个字符串,并给与初始值
    s := "Hello World!"
    // 对变量 s 进行从新赋值
    s := "Hello Go!"
}

那为什么这种场景下又能够给字符串从新赋值呢?
这是因为,在 Go 的底层其实是新创建了一个 []byte{} 类型的切片,将变量 s 中的指针指向了新的内存空间地址 (也就是这里的Hello Go!)。原有的Hello World! 内存空间会随着垃圾回收机制被回收掉。

为什么这么设计

可能大家都会思考到,为什么一个一般的字符串要设计这么简单,还须要应用指针。临时没找到官网文档的阐明,

  1. 集体猜测,当遇到一个十分长的字符时,这样做使得 string 变得十分轻量,能够很不便的进行传递而不必放心内存拷贝。尽管在 Go 中,不论是援用类型还是值类型参数传递都是值传递。但指针显著比值传递更节俭内存。
正文完
 0