深入理解 Go 语言中的字符串不可变性与底层实现

文章目录

  • 前言
  • 1 字符串类型的数据结构组成
  • 2 为什么要这么设计数据结构?
  • 3 为什么说字符串类型不可修改?
  • 4 如何实现字符串的修改?
  • 5 为什么字符串修改的字面量用单引号?
  • 6 如何判断字符串的修改新建了一个字符串?
  • 7 字符串的修改后新建字符串的场景有哪些?
  • 8 概要总结
  • 9 参考链接

前言

本文深入探讨了 Go 语言中字符串的不可变性及其底层实现
通过学习,我们将会理解为什么字符串设计为不可变的原因,以及如何判断字符串在修改后的底层数据地址是否发生变化,以确定是否创建了新的字符串。


1 字符串类型的数据结构组成

Go 字符串类型的数据结构包括:一个指向底层字节数组的指针和一个字符串长度的整数值
这个字节数组是不可变的,一旦字符串被创建,字符串的内容将无法被修改

在这里插入图片描述


type stringStruct struct {str unsafe.Pointer // 指向底层字节数组的指针len int            // 字符串长度
}

2 为什么要这么设计数据结构?

  1. 保证线程安全:不可变字符串是线程安全的,只允许读操作,在并发场景下无需担心数据竞争问题。
  2. 实现内存共享:相同的字符串只需存储一次,实现多次重复使用,节省内存。
  3. 优化性能:作为哈希表的键时,不需要每次重新计算哈希值,提高性能。

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的字符串类型不能修改

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/845516.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

c基础 - 输入输出

目录 一.scanf() 和 printf() 函数 1.printf 2.scanf 二 . getchar() & putchar() 函数 1.int getchar(void) 2.int putchar(int c) 三. gets() & puts() 函数 一.scanf() 和 printf() 函数 #include <stdio.h> 需要引入头文件,stdio.h 1.printf print…

M-G364PD惯性测量单元:相机及微小层面的革命性应用

在现代科技飞速发展的今天&#xff0c;精准控制和精确测量是众多高端设备实现卓越性能的关键。爱普生推出的M-G364PD惯性测量单元&#xff08;IMU&#xff09;&#xff0c;因其卓越的性能和微小尺寸&#xff0c;成为相机以及其他微小层面应用的理想选择&#xff0c;为科技创新提…

存储 Bean 对象更加简单的方式

前置操作 如果是在 spring-config 中添加 bean 标签来注册内容&#xff0c;每个类都要弄一次就显得麻烦和臃肿了&#xff0c;对于 new 操作而言就没有什么优势了。因此 spring 就引入了注解操作来实现对 Bean 对象的存储。 配置扫描路径 想要将对象成功的存储到 Spring 中&…

python onnxruntime DLL load failed: 找不到指定的模块

在安装ddddocr 报错&#xff1a;ImportError: DLL load failed while importing onnxruntime_pybind11_state: 找不到指定的模块 试了降到onnxruntime 1.8.0版本&#xff0c;升级pip再重装ddddocr均无效。 这个错误通常是因为缺少onnxruntime_pybind11_state.dll文件或者没有…

Liunx中使用他人身份来执行命令或新建文件

前言 在一些情况下。我们想要借助某个用户的身份来执行命令或者新建文件&#xff0c; 比如某个用户的bash是 nologin 或者 false。 该怎么做呢&#xff1f;&#xff1f; 答&#xff1a;使用 sudo -u 即可。 例如&#xff1a; sudo -u ygz1 touch temp1.txt哈哈哈&#xff0…

android gradle8.3 发布插件踩过的坑

之前写过gradle6.x和gradle7.x的插件&#xff0c;会有一些改动&#xff0c;到8.x我发现又有一些变化&#xff0c;记录一下&#xff0c;防止后边再遇到相同的情况 下边是插件的gradle文件配置 plugins {id("java-gradle-plugin") //会自动引入java-library、gradleAp…

摄影后期照片编辑工具:LrC2024 for Mac/win 中文激活版

LrC2024&#xff08;Lightroom Classic 2024&#xff09;是 Adobe 公司推出的一款专业级别的照片编辑和管理软件。它是 Lightroom Classic CC 的升级版&#xff0c;具有更多的功能和改进。 这款软件主要用于数字摄影师和摄影爱好者处理、编辑和管理他们的照片。它提供了一套强大…

【网络安全的神秘世界】在win11搭建pikachu靶场

&#x1f31d;博客主页&#xff1a;泥菩萨 &#x1f496;专栏&#xff1a;Linux探索之旅 | 网络安全的神秘世界 | 专接本 下载pikachu压缩包 https://github.com/zhuifengshaonianhanlu/pikachu 下载好的pikachu放在phpstudy_pro/www目录下 创建pikachu数据库 打开phpstudy软件…

独立开发的轻量级简洁开源论坛BBS PHP源码

最新的轻量级开源论坛php源码发布啦&#xff01;这是一款独立开发的论坛系统&#xff0c;可以帮助你快速地开发出你想要的网站。 如果你是PHP初学者&#xff0c;这款论坛系统非常适合你入门学习。不过&#xff0c;需要注意的是&#xff0c;由于它并没有进行商业化改造&#xf…

docker一键部署EFK系统(elasticsearch filebeat kibana metricbeat es-head)

EFK日志系统搭建 EFK日志系统介绍功能需求搭建elasticsearch集群规划前提部署核对证书及权限 EFK日志系统介绍 Elasticsearch 是一个实时的、分布式的可扩展的搜索引擎&#xff0c;允许进行全文、结构化搜索&#xff0c;它通常用于索引和搜索大量日志数据&#xff0c;也可用于…

【ARM-Linux篇】u-boot编译

一、u-boot简介 uboot是一种通用的引导加载程序&#xff0c;它可以用于多种嵌入式系统&#xff0c;支持多种操作系统&#xff0c;如Linux, Android,NetBSD等。uboot的主要作用是将操作系统内核从存储设备&#xff08;如Flash, SD卡等&#xff09;加载到内存中&#xff0c;并执…

一个令人惊艳的开源技术盛宴,来了!

公众号关注 “GitHubDaily” 设为 “星标”&#xff0c;每天带你逛 GitHub&#xff01; 在 GitHub 上&#xff0c;每天都会诞生各种实用的开源项目&#xff0c;这些项目在不同行业与领域得到充分应用&#xff0c;推动技术发展&#xff0c;让普通人也能够真切感受到科技魅力。 而…

使用 EBS 和构建数据库服务器并使用应用程序与数据库交互

实验 4&#xff1a;使用 EBS 实验概览 本实验着重介绍 Amazon Elastic Block Store (Amazon EBS)&#xff0c;这是一种适用于 Amazon EC2 实例的重要底层存储机制。在本实验中&#xff0c;您将学习如何创建 Amazon EBS 卷、将其附加到实例、向卷应用文件系统&#xff0c;然后进…

ES 生命周期管理

一 .概念 ILM定义了四个生命周期阶段&#xff1a;Hot&#xff1a;正在积极地更新和查询索引。Warm&#xff1a;不再更新索引&#xff0c;但仍在查询。cold&#xff1a;不再更新索引&#xff0c;很少查询。信息仍然需要可搜索&#xff0c;但是如果这些查询速度较慢也可以。Dele…

TOPIAM数字身份管控平台前端技术实践

一、引言 随着企业信息化程度的不断加深&#xff0c;内部办公系统、业务系统及三方SaaS系统的集成与整合成为企业面临的重要挑战之一。特别是如何有效管理员工账号、权限、身份认证以及应用访问&#xff0c;成为保障企业信息安全、提升用户体验的关键。TOPIAM数字身份管控平台…

VSCode Prettier - Code formatter 代码格式化

格式化代码是一个挑战&#xff0c;但现代开发工具可以自动保持团队代码库的一致性。 在本文中&#xff0c;您将设置 Prettier 以自动格式化 Visual Studio Code&#xff08;也称为 VS Code&#xff09;中的代码。 1. 安装插件 2. 定义代码风格 在项目根目录下创建一个pretti…

【NPS】微软NPS配置802.1x,验证域账号,动态分配VLAN(有线网络篇)

上两篇中介绍了如何配置NPS和在WLC上如何配置802.1X来实现验证域账号和动态分配VLAN&#xff0c;802.1x协议作为一种成熟的身份验证框架&#xff0c;不仅适用于无线网络&#xff0c;同样也适用于有线网络环境。这里我们将介绍如何在有线网络中部署802.1x认证&#xff0c;以验证…

Android Coil的简单介绍及使用

前言&#xff1a; 本文是借鉴网上大佬的Coil相关技术文章及结合自己项目中的实际使用情况&#xff0c;对Coil作一个简单介绍。 简介&#xff1a; Coil是一个Android的图片加载框架库&#xff0c;是通过Kotlin协程的方式加载图片的&#xff0c;相对于Glide、Picasso、Fresco等…

返回值

自学python如何成为大佬(目录):https://blog.csdn.net/weixin_67859959/article/details/139049996?spm1001.2014.3001.5501 到目前为止&#xff0c;我们创建的函数都只是为我们做一些事&#xff0c;做完了就结束。但实际上&#xff0c;有时还需要对事情的结果进行获取。这类…

使用Redis常遇到的问题

文章目录 概述缓存雪崩、穿透、击穿大key问题热Key问题缓存和数据库双写一致性问题缓存并发竞争Redis线上阻塞要如何排查Redis 常见的性能问题都有哪些Redis 如何做内存优化Redis数据倾斜 概述 在使用Redis时&#xff0c;有几个常见的问题可能会出现&#xff0c;包括但不限于以…