首页 > 其他分享 >为什么都说Go字符串类型不能修改

为什么都说Go字符串类型不能修改

时间:2022-10-28 20:33:18浏览次数:70  
标签:修改 字符串 Go byte Hello 赋值

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

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

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

本文已收录GiteeGithub。分享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中,不管是引用类型还是值类型参数传递都是值传递。但指针明显比值传递更节省内存。

标签:修改,字符串,Go,byte,Hello,赋值
From: https://blog.51cto.com/u_10992108/5802014

相关文章

  • 使用AHK将win修改为鼠标中键
    在使用UGNX绘图时,由于多数鼠标中键与滚轮为同一物理按键,且不方便按下,因此使用AHK将左win键改为鼠标中键AHK安装ahk是免费且开源的,大小仅有3MB,可在官网直接下载。官网......
  • django中的缓存机制
    前端混合开发缓存的使用-缓存的位置,通过配置文件来操作(以文件为例)-缓存的粒度: -全站缓存 中间件MIDDLEWARE=['djang......
  • MongoDB--常用语句
    1、插入单条数据db.students.insertOne({name:'Jane',age:21})2、插入多条数据db.students.insertMany([{name:'Kang',age:22},{name:'Mike',age:19}])3、删除单条数......
  • JavaScript--字符串
    一、字符串的概述1、String(字符串)数据类型表示零或多个16位Unicode字符序列。字符串可以使用双引号(")、单引号(')或反引号(`)标示。2、ECMAScript中的字符串是不可变的(im......
  • winform 双击表格里的数据进行修改
    表格触发事件publicList<Models.CountryList>uplist=newList<Models.CountryList>();privatevoiddataGridView1_CellValueChanged(objectsender,DataGridV......
  • Golang基础-流程控制
    流程控制语句是用来控制程序中各语句执行顺序的语句,可以把语句组合成能完成一定功能的小逻辑模块控制语句分为三大类:顺序、选择、循环一、分支结构if分支结构单分支......
  • Linq 分组GoupBy并且求和Sum
     Linq分组GoupBy并且求和SumList<Order>orderList=newList<Order>();for(inti=0;i<3;i++){OrderorderEntit......
  • MSSQL将时间类型转换为字符串类型
    示例代码如下: SelectCONVERT(varchar(100),GETDATE(),0):0516200610:57AMSelectCONVERT(varchar(100),GETDATE(),1):05/16/06SelectCONVERT(varchar(100),......
  • linux 中查找过去一段时间内被修改过的文件
     001、[root@pc1test]#lsa.txtb.txtc.txt[root@pc1test]#ll-h##测试文件total4.0K-rw-r--r--.1rootroot0Oct2816:36a.txt-rw-r......
  • 删除字符串尾部重复的“字符串”
    使用.net自带的方法TrimEnd(char[])也可以删除,但是有些情况不是我们想要的结果如:source=bx20x0x0x0;delStr=x0;source=source.TrimEnd("x0".ToArry());这时source......