Go string 实现原理剖析(你真的了解string吗)

string标准概念

Go标准库builtin给出了所有内置类型的定义。 源代码位于src/builtin/builtin.go,其中关于string的描述如下:

// string is the set of all strings of 8-bit bytes, conventionally but not
// necessarily representing UTF-8-encoded text. A string may be empty, but
// not nil. Values of string type are immutable.
type string string

所以string是8比特字节的集合,通常但并不一定是UTF-8编码的文本。

另外,还提到了两点,非常重要:

  • string可以为空(长度为0),但不会是nil;
  • string对象不可以修改。

string 数据结构

源码包src/runtime/string.go:stringStruct定义了string的数据结构:

type stringStruct struct {
	str unsafe.Pointer
	len int
}

其数据结构很简单:

  • stringStruct.str:字符串的首地址;
  • stringStruct.len:字符串的长度;

string数据结构跟切片有些类似,只不过切片还有一个表示容量的成员,事实上string和切片,准确的说是byte切片经常发生转换。这个后面再详细介绍。

string操作

声明

如下代码所示,可以声明一个string变量变赋予初值:

    var str string
    str = "Hello World"

字符串构建过程是先跟据字符串构建stringStruct,再转换成string。转换的源码如下:

func gostringnocopy(str *byte) string { // 跟据字符串地址构建string
	ss := stringStruct{str: unsafe.Pointer(str), len: findnull(str)} // 先构造stringStruct
	s := *(*string)(unsafe.Pointer(&ss))                             // 再将stringStruct转换成string
	return s
}

string在runtime包中就是stringStruct,对外呈现叫做string。

[]byte转string

byte切片可以很方便的转换成string,如下所示:

func GetStringBySlice(s []byte) string {
    return string(s)
}

需要注意的是这种转换需要一次内存拷贝。

转换过程如下:

  1. 跟据切片的长度申请内存空间,假设内存地址为p,切片长度为len(b);
  2. 构建string(string.str = p;string.len = len;)
  3. 拷贝数据(切片中数据拷贝到新申请的内存空间)

转换示意图:

string转[]byte

string也可以方便的转成byte切片,如下所示:

func GetSliceByString(str string) []byte {
    return []byte(str)
}

string转换成byte切片,也需要一次内存拷贝,其过程如下:

  • 申请切片内存空间
  • 将string拷贝到切片

转换示意图:

字符串拼接

字符串可以很方便的拼接,像下面这样:

str := "Str1" + "Str2" + "Str3"

即便有非常多的字符串需要拼接,性能上也有比较好的保证,因为新字符串的内存空间是一次分配完成的,所以性能消耗主要在拷贝数据上。

一个拼接语句的字符串编译时都会被存放到一个切片中,拼接过程需要遍历两次切片,第一次遍历获取总的字符串长度,据此申请内存,第二次遍历会把字符串逐个拷贝过去。

字符串拼接伪代码如下:

func concatstrings(a []string) string { // 字符串拼接
    length := 0        // 拼接后总的字符串长度

    for _, str := range a {
        length += length(str)
    }

    s, b := rawstring(length) // 生成指定大小的字符串,返回一个string和切片,二者共享内存空间

    for _, str := range a {
        copy(b, str)    // string无法修改,只能通过切片修改
        b = b[len(str):]
    }
    
    return s
}

因为string是无法直接修改的,所以这里使用rawstring()方法初始化一个指定大小的string,同时返回一个切片,二者共享同一块内存空间,后面向切片中拷贝数据,也就间接修改了string。

rawstring()源代码如下:

func rawstring(size int) (s string, b []byte) { // 生成一个新的string,返回的string和切片共享相同的空间
	p := mallocgc(uintptr(size), nil, false)

	stringStructOf(&s).str = p
	stringStructOf(&s).len = size

	*(*slice)(unsafe.Pointer(&b)) = slice{p, size, size}

	return
}

为什么字符串不允许修改?

像C++语言中的string,其本身拥有内存空间,修改string是支持的。但Go的实现中,string不包含内存空间,只有一个内存的指针,这样做的好处是string变得非常轻量,可以很方便的进行传递而不用担心内存拷贝。

因为string通常指向字符串字面量,而字符串字面量存储位置是只读段,而不是堆或栈上,所以才有了string不可修改的约定。

[]byte转换成string一定会拷贝内存吗?

byte切片转换成string的场景很多,为了性能上的考虑,有时候只是临时需要字符串的场景下,byte切片转换成string时并不会拷贝内存,而是直接返回一个string,这个string的指针(string.str)指向切片的内存。

比如,编译器会识别如下临时场景:

  • 使用m[string(b)]来查找map(map是string为key,临时把切片b转成string);
  • 字符串拼接,如"<" + "string(b)" + ">";
  • 字符串比较:string(b) == "foo"

因为是临时把byte切片转换成string,也就避免了因byte切片同容改成而导致string引用失败的情况,所以此时可以不必拷贝内存新建一个string。

string和[]byte如何取舍

string和[]byte都可以表示字符串,但因数据结构不同,其衍生出来的方法也不同,要跟据实际应用场景来选择。

string 擅长的场景:

  • 需要字符串比较的场景;
  • 不需要nil字符串的场景;

[]byte擅长的场景:

  • 修改字符串的场景,尤其是修改粒度为1个字节;
  • 函数返回值,需要用nil表示含义的场景;
  • 需要切片操作的场景;

虽然看起来string适用的场景不如[]byte多,但因为string直观,在实际应用中还是大量存在,在偏底层的实现中[]byte使用更多。

赠人玫瑰手留余香,如果觉得不错请给个赞~

本篇文章已归档到GitHub项目,求星~ 点我即达

转载于:https://my.oschina.net/renhc/blog/3019849

weixin_33769207
关注 关注
  • 10
    点赞
  • 19
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Go语言string底层结构与实战
分享代码与人生
02-25 534
学会golangstring底层结构,掌握string用法,并避坑,让你彻底掌握string用法。
golang实现unicode转换为字符串string的方法
09-21
主要介绍了golang实现unicode转换为字符串string的方法,实例分析了Go语言编码转换的相关技巧,具有一定参考借鉴价值,需要的朋友可以参考下
golang中的string与其他格式数据的转换方法
最新发布
ens160的博客
10-23 324
strconv包下有多个方法,可以实现string to int 类型,但是strconv.Parse*可以实现更多的格式。
Go1.19.3 string原理简析
Go的全部
12-12 931
Go1.19.3 string原理简析
GoLangstring底层系列三(底层)
weixin_52690231的博客
05-12 584
GoLangstring底层系列三(底层)
GoLangstring底层系列二(基础)
weixin_52690231的博客
05-12 385
GoLangstring底层系列二(基础)
GO语言映射(Map)用法分析
12-26
本文实例讲述了GO语言映射(Map)用法。分享给大家供大家参考。具体如下: 映射是一种内置的数据结构,用来保存键值对的无序集合。 (1)映射的创建 make ( map [KeyType] ValueType, initialCapacity ) make ( map ...
浅谈go语言renderer包代码分析
01-03
本文绕开如何使用它,深入到代码实现中研究它,同时也尝尝Go语言包的开发套路。 Go包基础介绍 代码结构 package pkgname import ( fmt ... ) const ( CONST1 typeX = xx ... ) var ( VAR1 typeX = xxx ... ...
Go语言map字典用法实例分析
01-01
本文实例讲述了Go语言map字典用法。分享给大家供大家参考。具体分析如下: 这段代码生成了青岛、济南、烟台三个城市拼音和汉字的对照字典,根据拼音可以输出汉字 代码如下:package main import “fmt” func main()...
GO语言Defer用法实例分析
01-20
本文实例讲述了GO语言Defer用法。分享给大家供大家参考。具体分析如下: defer:调用一个被 defer 的函数时在函数刚要返回之前延迟执行,当函数无论怎样返回,某资源必须释放时,可用这种与众不同、但有效的处理方式...
Go语言模型:string的底层数据结构与高效操作
Life runs on code
11-25 5411
Golangstring类型底层数据结构简单,本质也是一个结构体实例,且是const不可变。 string的底层数据结构 通过下面一个例子来看: package main import ( &amp;amp;amp;quot;fmt&amp;amp;amp;quot; &amp;amp;amp;quot;unsafe&amp;amp;amp;quot; ) // from: string.go 在GoLand IDE中双击shift快速找到 type stringS
GoLangstring底层系列一(UTF-8编码)
weixin_52690231的博客
03-05 2044
GoLang之UTF编码与string底层
Go中的string及相关操作
weixin_44718747的博客
11-30 1298
package main import ( "bytes" "fmt" ) func main() { s1 := "hello" s2 := "world" // 借助字节缓冲区byteBuffer来实现类似Java中StringBuilder的效果,因为在go中string底层是由byte数组实现的 // byte即为uint8类型,所以用byte来存储字符时保存的是其ascii码,同理string底层的byte数组实际上也是存储的该string的每个字符对应的ascii码 Strin
深入理解golang string
maybe的博客
05-19 1223
golang string string的定义 // string is the set of all strings of 8-bit bytes, conventionally but not // necessarily representing UTF-8-encoded text. A string may be empty, but // not nil. Values of string type are immutable. type string string string里存储的是字符
Go语言的string(底层结构+常用方法)
weixin_45514135的博客
05-08 3046
字符串 Go语言中的字符串是通过UTF-8编码,字符串的值为双引号(")中的内容,可以在Go语言的源码中直接添加非ASCII码字符 。 字符串底层结构是一个起始地址和长度(字节个数) 字符串转义符 Go 语言的字符串常见转义符包含回车、换行、单双引号、制表符等 转义符 含义 /r 回车符 /n 换行符 /t 制表符 /’ 单引号 /" 双引号 // 反斜杠 例如: //打印一个Windows平台下的一个文件路径 package main import ( "
面试官:String的最大长度是多少?
20年码农
12-17 7988
之前有提到,指北君在做面试相关的事情。有面试官问了String的最大长度是多少?指北君听到这个问题之后有点懵,还好指北君抗住了。 指北君:面试官你好,可以开始面试了吗? 面试官:你好,那我们现在开始吧。之前我们聊了new object()到底占用多少个字节?看你对JVM有一定的了解。那我今天问问你,一个 String 字符串最大长度是多少? 指北君:从我们平常使用的角度看,String 字符串似乎是没有长度限制。所以也不存在最大长度这个事情。 面试官:真的这样吗?是这样的话,我们今天可以结束了。不用
go string的内部实现
weixin_40581617的博客
10-24 793
这个string的探索 来来个例子 func boo(a int, b int)(int, string){ return a + b, "abcd" } 81079 000000000044dfa0 &lt;main.boo&gt;: 81080 44dfa0:&gt;------48 c7 44 24 18 00 00 &gt;--movq $0x0,0x18(%rsp...
String +操作底层原理
Burton的博客
04-01 1221
在阿里巴巴开发手册中说明在for循环中不要使用String+操作,而推荐使用Stringbuilder,多线程则可以使用Stringbuffer 简单写了一个个demo: @Test public void testString() { String a = "AAA"; String b = "BBB"; String c = a+b...
type Service[] struct { Id string `json:"id"` Title string `json:"title"` Url string `json:"url"` ImgUrl string `json:"img_url"` ExtendContent string `json:"extend_content"` UrlType int64 `json:"url_type"` TypeId int64 `json:"type_id"` Keyword string `json:"keyword"` IsLogin int `json:"is_login"` } if ($result['service'] != false) { foreach ($result['service'] as $key => $se) { if (strpos($se['title'], '合伙') !== false && isset($result['show_retail']) && $result['show_retail'] == 0) { unset($result['service'][$key]); } if (strpos($se['title'], 'A码') !== false) { unset($result['service'][$key]); } } $result['service'] = array_values($result['service']); } 分析这段代码并用go改写
05-31
下面是用Go语言改写的代码: ```go type Service struct { Id string `json:"id"` Title string `json:"title"` Url string `json:"url"` ImgUrl string `json:"img_url"` ExtendContent string `json:"extend...

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
写文章

热门文章

  • 【Python】TypeError: can only concatenate list (not "int") to list 25448
  • SQL delete语句 之多表删除 总结 19867
  • ESXi 6.7交换机做端口聚合Ethernet Channel后网络不通的解决方法 13688
  • 勒索病毒免费解密工具都在这里! 10309
  • Caution: request is not finished yet 10288

最新评论

  • 用vbs打开文件

    m0_69459938: vba调用bat打开文件夹

  • 校验知识:CRC32、MD5、SHA1概念及可靠性现状

    huzhiyang555: 最后一句话是亮点,哈哈

  • 拒绝“割韭菜”— 谈谈区块链正经的商用场景!

    LeoScott613: 目前区块链在公司管理方面的应用还是不够成熟。我认为如果能提出一个方案让大部分公司应用区块链,那么区块链就会非常成熟

  • 怎样取消Macbook上顽固的开机启动项

    2201_75488681: 感谢分享,想问下 /Library/LaunchAgents/这个步骤怎么打开的呢?

  • 启动hbase时出现HMaster Aborted错误

    m0_60378984: cdh的怎么删呢?zookeeper客户端命令进不去

您愿意向朋友推荐“博客详情页”吗?

  • 强烈不推荐
  • 不推荐
  • 一般般
  • 推荐
  • 强烈推荐
提交

最新文章

  • iOS 和服务端交互 数据加密策略
  • 269. Alien Dictionary
  • 【转】40个UI设计工具和资源
2019年364篇
2018年710篇
2017年955篇
2016年567篇
2015年439篇
2014年317篇
2013年319篇
2012年277篇
2011年212篇
2010年160篇
2009年125篇
2008年112篇
2007年79篇
2006年34篇
2005年18篇
2004年2篇

目录

目录

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值

天下网TXWEB固原营销网站建设报价北京市seo按天扣费哪家专业南通优化广州网站定制报价关键词排名价格安阳阿里店铺运营推荐东莞建网站报价广元市seo哪家好许昌网站搭建推荐金华网页制作哪家专业惠州市网站定制推荐大连市网站改版濮阳市网站建设安阳定制网站公司淄博市网站定制公司辽阳市网站搭建推荐渭南市阿里店铺运营哪家好商丘市网站开发哪家专业兴安盟网站推广哪家好兰州网站开发公司福州企业网站改版多少钱崇左网站搭建公司营口市网站定制价格定西市网站开发衡水做网站濮阳品牌网站设计价格晋中网站开发哪家专业南联网站设计推荐保山品牌网站设计报价蚌埠市品牌网站设计推荐香港通过《维护国家安全条例》两大学生合买彩票中奖一人不认账让美丽中国“从细节出发”19岁小伙救下5人后溺亡 多方发声卫健委通报少年有偿捐血浆16次猝死汪小菲曝离婚始末何赛飞追着代拍打雅江山火三名扑火人员牺牲系谣言男子被猫抓伤后确诊“猫抓病”周杰伦一审败诉网易中国拥有亿元资产的家庭达13.3万户315晚会后胖东来又人满为患了高校汽车撞人致3死16伤 司机系学生张家界的山上“长”满了韩国人?张立群任西安交通大学校长手机成瘾是影响睡眠质量重要因素网友洛杉矶偶遇贾玲“重生之我在北大当嫡校长”单亲妈妈陷入热恋 14岁儿子报警倪萍分享减重40斤方法杨倩无缘巴黎奥运考生莫言也上北大硕士复试名单了许家印被限制高消费奥巴马现身唐宁街 黑色着装引猜测专访95后高颜值猪保姆男孩8年未见母亲被告知被遗忘七年后宇文玥被薅头发捞上岸郑州一火锅店爆改成麻辣烫店西双版纳热带植物园回应蜉蝣大爆发沉迷短剧的人就像掉进了杀猪盘当地回应沈阳致3死车祸车主疑毒驾开除党籍5年后 原水城县长再被查凯特王妃现身!外出购物视频曝光初中生遭15人围殴自卫刺伤3人判无罪事业单位女子向同事水杯投不明物质男子被流浪猫绊倒 投喂者赔24万外国人感慨凌晨的中国很安全路边卖淀粉肠阿姨主动出示声明书胖东来员工每周单休无小长假王树国卸任西安交大校长 师生送别小米汽车超级工厂正式揭幕黑马情侣提车了妈妈回应孩子在校撞护栏坠楼校方回应护栏损坏小学生课间坠楼房客欠租失踪 房东直发愁专家建议不必谈骨泥色变老人退休金被冒领16年 金额超20万西藏招商引资投资者子女可当地高考特朗普无法缴纳4.54亿美元罚金浙江一高校内汽车冲撞行人 多人受伤

天下网TXWEB XML地图 TXT地图 虚拟主机 SEO 网站制作 网站优化