深入理解 Go 语言中的字符串不可变性与底层实现
文章目录
- 前言
- 1 字符串类型的数据结构组成
- 2 为什么要这么设计数据结构?
- 3 为什么说字符串类型不可修改?
- 4 如何实现字符串的修改?
- 5 为什么字符串修改的字面量用单引号?
- 6 如何判断字符串的修改新建了一个字符串?
- 7 字符串的修改后新建字符串的场景有哪些?
- 8 概要总结
- 9 参考链接
前言
本文深入探讨了 Go 语言中字符串的不可变性及其底层实现。
通过学习,我们将会理解为什么字符串设计为不可变的原因,以及如何判断字符串在修改后的底层数据地址是否发生变化,以确定是否创建了新的字符串。
1 字符串类型的数据结构组成
Go 字符串类型的数据结构包括:一个指向底层字节数组的指针和一个字符串长度的整数值。
这个字节数组是不可变的,一旦字符串被创建,字符串的内容将无法被修改。

type stringStruct struct {str unsafe.Pointer // 指向底层字节数组的指针len int // 字符串长度
}
2 为什么要这么设计数据结构?
- 保证线程安全:不可变字符串是线程安全的,只允许读操作,在并发场景下无需担心数据竞争问题。
- 实现内存共享:相同的字符串只需存储一次,实现多次重复使用,节省内存。
- 优化性能:作为哈希表的键时,不需要每次重新计算哈希值,提高性能。
3 为什么说字符串类型不可修改?
字符串底层是只读的字节序列,任何对字符串的修改实际上都会创建一个新的字符串,而不会改变原始字符串
# 错误示范:导致编译报错!!
str := "hello"
str = "Hello"//字符串是不可修改的,是不允许直接在原字符串上操作的
fmt.Prtintln(str)
4 如何实现字符串的修改?
由于字符串是不可修改的,实际的字符串修改操作是创建了一个新的字符串。

常见的做法:先将字符串转换为 []byte 或者 []rune,进行修改操作后,再转换为字符串。
// 初始字符串:使用双引号表示字符串
str := "hello"
fmt.Println("旧字符串:%v",str)// 将字符串转换为[]byte切片
strBytes := []byte(str)
// 修改 []byte 中的一个字符,使用单引号表示字符
strBytes[0] = 'H'
str = string(strBytes) // 创建了一个新的字符串
fmt.Println("新字符串:%v",str)
5 为什么字符串修改的字面量用单引号?
[!question]+
strBytes[0] = 'H'使用了单引号,为什么需要使用单引号?
- 单引号字面量,代表单个字符,常用于[]byte和[]rune中的字符元素修改。
- 双引号和反引号的字面量,代表字符串
6 如何判断字符串的修改新建了一个字符串?
[!warning]+ 判断依据: 字符串修改操作会创建一个新的字符串,并将底层的指针地址指向新字符串。如果要判断 Go 字符串修改是否创建了新字符串,需要判断字符串内容的地址前后是否一致。
我们知道,获取一个变量的地址有两种方式:①使用
unsafe包 ②使用fmt.Printf("%p", &s)。这两种方式对于获取字符串变量地址有所差异,第一种方式获取的是底层字节数组的地址,第二种方式获取的是字符串变量本身的地址。以下是代码示例:
// 获取字符串的指针地址
func getStringPointer(s string) uintptr { return uintptr(unsafe.Pointer(&s))
} func main() { // 初始字符串 s := "hello" // 获取初始字符串的指针地址 initialPointer := getStringPointer(s) // 打印指针地址 fmt.Printf("Initial pointer: %x\n", initialPointer) // 将字符串转换为 []byte b := []byte(s) // 修改 []byte b[0] = 'H' // 将 []byte 转回字符串,并给修改字符串s s = string(b) // 获取新字符串的指针地址 newPointer := getStringPointer(s) fmt.Printf("New pointer: %x\n", newPointer) // 判断是否创建了新字符串 if initialPointer != newPointer { fmt.Println("新字符串已创建") } else { fmt.Println("没有创建新字符串") }
}
7 字符串的修改后新建字符串的场景有哪些?
每次对字符串的修改操作(字符串拼接、字符串替换、切片操作),都会创建一个新的字符串。
8 概要总结
[!example]+ 概要总结
- 我们从GO字符串的底层数据结构了解到,字符串是不可修改的,原因是字符串底层是只读的字节序列,若直接在原字符串修改,则编译器将引发错误。
- 想要修改字符串就必须转换为[]byte或者[]rune,修改之后转换为原有字符串类型。
- []byte或者[]rune的修改的字面量必须使用单引号,双引号是代表的字符串。
- 通过代码分析可知,字符串修改操作会创建一个新的字符串,并将底层的指针地址指向新字符串。
9 参考链接
- 图片引用1:Go 数据结构
- 图片引用2:为什么说Go的字符串类型不能修改
相关文章:
深入理解 Go 语言中的字符串不可变性与底层实现
文章目录 前言1 字符串类型的数据结构组成2 为什么要这么设计数据结构?3 为什么说字符串类型不可修改?4 如何实现字符串的修改?5 为什么字符串修改的字面量用单引号?6 如何判断字符串的修改新建了一个字符串?7 字符串的…...
采购订单审批和取消例子
文章目录 1 Introduction2 Example 1 Introduction This is a exmaple for releaseing po and reseting po. 2 Example DATA:lw_in TYPE zmms015,lw_out TYPE zmms015_out,lt_head LIKE TABLE OF ZMMT003_head,lw_head TYPE ZMMT003_head,lt_item TYPE zmmt003_item_t,lt…...
PHP:集成Xunsearch生成前端搜索骨架
如果是安装宝塔,我们在集成xunsearch的时候就会比较简单,后面我们在介绍其他的接入方式; 首先我们进入到宝塔管理后台:【软件商店】-【输入xun】-【点击xunsearch】直接安装即可 安装成功之后,会自动在www/server中创…...
ThreadLocal详解,与 HashMap 对比
ThreadLocal原理,使用注意事项,解决哈希冲突方式->和HashMap对比 ThreadLocal 原理: ThreadLocal 是 Java 中的一个线程级别的变量,它允许您在不同线程之间存储和访问相同变量的不同副本,每个线程都拥有自己的副本&…...
flask流式接口
一、接口封装 from flask import Flask, request, Response, stream_with_context app Flask(__name__) app.logger.disabled Truedef chat_stream_(prompt):for new_text in [1,2,3]:yield new_textapp.route(/chat_stream, methods[POST]) def chat_stream():prompt requ…...
MatLab命令行常用命令记录
文章目录 MatLab常用命令行MatLab常用按键标点说明 MatLab常用命令行 Matlab常用命令用来管理目录、命令、函数、变量、工作区、文件及窗口。常用命令如下表 命令作用cd显示或改变当前文件夹load加载指定文件的变量dir显示当前文件夹或指定目录下的文件diary日志文件命令type…...
Linux —— MySQL操作(1)
一、用户与权限管理 1.1 创建与赋予权限 create user peter% identified by 123465 # 创建用户 peter,# %:允许所有用户登录这个用户访问数据库 刚创建的新用户是什么权限都没有,需要赋予权限 grant select on mysql.* to peter%; # 赋予…...
TCP四次握手与http协议版本区别
TCP四次挥手(图解)-为何要四次挥手 当客户端和服务器通过三次握手建立了TCP连接以后,当数据传送完毕,肯定是要断开TCP连接的啊。那 对于TCP的断开连接,这里就有了神秘的“四次挥手”。 第一次挥手:主机1(可以使客户端…...
【机器学习】洞悉数据奥秘:决策树算法在机器学习中的魅力
在机器学习的分类和回归问题中,决策树是一种广泛使用的算法。决策树模型因其直观性、易于理解和实现,以及处理分类和数值特征的能力而备受欢迎。本文将解释决策树算法的概念、原理、应用、优化方法以及未来的发展方向。 🚀时空传送门 &#x…...
redis(17):什么是布隆过滤器?如何实现布隆过滤器?
1 布隆过滤器介绍 布隆过滤器(Bloom Filter)是一种空间效率极高的概率型数据结构,用于判断一个元素是否在一个集合中。它基于位数组和多个哈希函数的原理,可以高效地进行元素的查询,而且占用的空间相对较小,如下图所示: 根据 key 值计算出它的存储位置,然后将此位置标…...
STM32自己从零开始实操03:输出部分原理图
一、继电器电路 1.1指路 延续使用 JZC-33F-012-ZS3 继电器,设计出以小电流撬动大电流的继电器电路。 (提示)电路需要包含:三极管开关电路、续流二极管、滤波电容、指示灯、输出部分。 1.2数据手册重要信息提炼 联系排列&…...
Unity中将图片做成Prefab的步骤
Unity中将图片做成Prefab的步骤 在Unity中,将一张图片做成Prefab是一个常见的操作。Prefab是Unity中的一种资源类型,可以让你预先定义一个游戏对象,然后在场景中多次实例化它。以下是详细的步骤: 步骤一:准备图片资源…...
Web前端三大主流框架:React、Vue和Angular
在当前的Web开发领域,前端框架的选择对于项目的成功至关重要。作为一名资深的IT技术员,我对前端技术的发展和行业趋势保持着持续的关注。本文将介绍当前Web前端三大主流框架:React、Vue和Angular,并分析它们各自的优势。 React&a…...
安全风险 - 检测设备是否为模拟器
在很多安全机构的检测中,关于模拟器的运行环境一般也会做监听处理,有的可能允许执行但是会提示用户,有的可能直接禁止在模拟器上运行我方APP 如何判断当前 app 是运行在Android真机,还是运行在模拟器? 可能做 Framework 的朋友思…...
maven的下载以及配置的详细教程(附网盘下载地址)
文章目录 下载配置IDEA内部使用配置 下载 1.百度网盘下载 链接: https://pan.baidu.com/s/1LD9wOMFalLL49XUscU4qnQ?pwd1234 提取码: 1234 2.解压即可 配置 1.打开安装文件下conf下的settings.xml文件,我的如下 2.修改配置信息(目的是为了修改本地…...
Unity3D 主城角色动画控制与消息触发详解
前言 在游戏开发中,角色动画控制和消息触发是非常重要的一部分,它可以让游戏更加生动和互动。本文将详细介绍如何在Unity3D中实现主城角色动画控制与消息触发。 对惹,这里有一个游戏开发交流小组,大家可以点击进来一起交流一下开…...
【Endnote】如何在word界面加载Endnote
如何在word界面加载Endnote 方法1:方法2:从word入手方法3:从CWYW入手参考 已下载EndNote,但Word中没有显示EndNote,应如何加载显示呢? 方法1: 使用EndNote的Configure EndNote.exe 。 具体步骤为&#x…...
优化CPU占用率及内存占用2
在标准化无线通信板时,关注过程序占用ram的问题,当时 发现每一个线程都会分配8M栈空间,这次换rk3568后,偶尔看了下RAM占用,吓了一跳,不但每个线程有8M栈空间,几乎每个线程都占用了64MB的一个RAM…...
C语言(字符和字符串函数)2
Hi~!这里是奋斗的小羊,很荣幸各位能阅读我的文章,诚请评论指点,欢迎欢迎~~ 💥个人主页:小羊在奋斗 💥所属专栏:C语言 本系列文章为个人学习笔记,在这里撰写成文一…...
【数据结构与算法 | 栈篇】力扣20,150
1. 力扣20 : 有效的符号 (1). 题 给定一个只包括 (,),{,},[,] 的字符串 s ,判断字符串是否有效。 有效字符串需满足: 左括号必须用相同类型的右括号闭合。左括号必须以正确的顺序闭合。每个…...
【Linux】shell脚本忽略错误继续执行
在 shell 脚本中,可以使用 set -e 命令来设置脚本在遇到错误时退出执行。如果你希望脚本忽略错误并继续执行,可以在脚本开头添加 set e 命令来取消该设置。 举例1 #!/bin/bash# 取消 set -e 的设置 set e# 执行命令,并忽略错误 rm somefile…...
中南大学无人机智能体的全面评估!BEDI:用于评估无人机上具身智能体的综合性基准测试
作者:Mingning Guo, Mengwei Wu, Jiarun He, Shaoxian Li, Haifeng Li, Chao Tao单位:中南大学地球科学与信息物理学院论文标题:BEDI: A Comprehensive Benchmark for Evaluating Embodied Agents on UAVs论文链接:https://arxiv.…...
Linux相关概念和易错知识点(42)(TCP的连接管理、可靠性、面临复杂网络的处理)
目录 1.TCP的连接管理机制(1)三次握手①握手过程②对握手过程的理解 (2)四次挥手(3)握手和挥手的触发(4)状态切换①挥手过程中状态的切换②握手过程中状态的切换 2.TCP的可靠性&…...
06 Deep learning神经网络编程基础 激活函数 --吴恩达
深度学习激活函数详解 一、核心作用 引入非线性:使神经网络可学习复杂模式控制输出范围:如Sigmoid将输出限制在(0,1)梯度传递:影响反向传播的稳定性二、常见类型及数学表达 Sigmoid σ ( x ) = 1 1 +...
【分享】推荐一些办公小工具
1、PDF 在线转换 https://smallpdf.com/cn/pdf-tools 推荐理由:大部分的转换软件需要收费,要么功能不齐全,而开会员又用不了几次浪费钱,借用别人的又不安全。 这个网站它不需要登录或下载安装。而且提供的免费功能就能满足日常…...
处理vxe-table 表尾数据是单独一个接口,表格tableData数据更新后,需要点击两下,表尾才是正确的
修改bug思路: 分别把 tabledata 和 表尾相关数据 console.log() 发现 更新数据先后顺序不对 settimeout延迟查询表格接口 ——测试可行 升级↑:async await 等接口返回后再开始下一个接口查询 ________________________________________________________…...
Git 3天2K星标:Datawhale 的 Happy-LLM 项目介绍(附教程)
引言 在人工智能飞速发展的今天,大语言模型(Large Language Models, LLMs)已成为技术领域的焦点。从智能写作到代码生成,LLM 的应用场景不断扩展,深刻改变了我们的工作和生活方式。然而,理解这些模型的内部…...
【Linux系统】Linux环境变量:系统配置的隐形指挥官
。# Linux系列 文章目录 前言一、环境变量的概念二、常见的环境变量三、环境变量特点及其相关指令3.1 环境变量的全局性3.2、环境变量的生命周期 四、环境变量的组织方式五、C语言对环境变量的操作5.1 设置环境变量:setenv5.2 删除环境变量:unsetenv5.3 遍历所有环境…...
tomcat入门
1 tomcat 是什么 apache开发的web服务器可以为java web程序提供运行环境tomcat是一款高效,稳定,易于使用的web服务器tomcathttp服务器Servlet服务器 2 tomcat 目录介绍 -bin #存放tomcat的脚本 -conf #存放tomcat的配置文件 ---catalina.policy #to…...
快速排序算法改进:随机快排-荷兰国旗划分详解
随机快速排序-荷兰国旗划分算法详解 一、基础知识回顾1.1 快速排序简介1.2 荷兰国旗问题 二、随机快排 - 荷兰国旗划分原理2.1 随机化枢轴选择2.2 荷兰国旗划分过程2.3 结合随机快排与荷兰国旗划分 三、代码实现3.1 Python实现3.2 Java实现3.3 C实现 四、性能分析4.1 时间复杂度…...
