问答文章1 问答文章501 问答文章1001 问答文章1501 问答文章2001 问答文章2501 问答文章3001 问答文章3501 问答文章4001 问答文章4501 问答文章5001 问答文章5501 问答文章6001 问答文章6501 问答文章7001 问答文章7501 问答文章8001 问答文章8501 问答文章9001 问答文章9501

详解Go空结构体的3种使用场景

发布网友 发布时间:2024-09-17 00:45

我来回答

1个回答

热心网友 时间:2024-12-12 08:04

大家好,我是煎鱼。在Go语言中,有一个比较特殊的类型,经常会有刚接触Go的小伙伴问到,又或是不理解。

他就是Go里的空结构体(struct)的使用,常常会有看到有人使用:

%&&&&&%0

还清一色的使用结构体,也不用其他类型。高度常见,也就不是一个偶发现象了,肯定是背后必然有什么原因。

今天煎鱼这篇文章带大家了解一下为什么要这么用,知其然知其所以然。

一起愉快地开始吸鱼之路。

为什么使用

说白了,就是希望节省空间。但,新问题又来了,为什么不能用其他的类型来做?

这就涉及到在Go语言中”宽度“的概念,宽度描述了一个类型的实例所占用的存储空间的字节数。

宽度是一个类型的属性。在Go语言中的每个值都有一个类型,值的宽度由其类型定义,并且总是8bits的倍数。

在Go语言中我们可以借助unsafe.Sizeof方法,来获取:

//Sizeoftakesanexpressionxofanytypeandreturnsthesizeinbytes//ofahypotheticalvariablevasifvwasdeclaredviavarv=x.//Thesizedoesnotincludeanymemorypossiblyreferencedbyx.//Forinstance,ifxisaslice,Sizeofreturnsthesizeoftheslice//descriptor,notthesizeofthememoryreferencedbytheslice.//ThereturnvalueofSizeofisaGoconstant.funcSizeof(xArbitraryType)uintptr

该方法能够得到值的宽度,自然而然也就能知道其类型对应的宽度是多少了。

我们对应看看Go语言中几种常见的类型宽度大小:

funcmain(){varaintvarbstringvarcboolvard[3]int32vare[]stringvarfmap[string]boolfmt.Println(unsafe.Sizeof(a),unsafe.Sizeof(b),unsafe.Sizeof(c),unsafe.Sizeof(d),unsafe.Sizeof(e),unsafe.Sizeof(f),)}

输出结果:

816112248

你可以发现我们列举的几种类型,只是单纯声明,我们也啥没干,依然占据一定的宽度。

如果我们的场景,只是占位符,那怎么办,系统里的开销就这么白白浪费了?

空结构体的特殊性

空结构体在各类系统中频繁出现的原因之一,就是需要一个占位符。而恰恰好,Go空结构体的宽度是特殊的。

如下:

funcmain(){varsstruct{}fmt.Println(unsafe.Sizeof(s))}

输出结果:

0

空结构体的宽度是很直接了当的0,即便是变形处理:

typeSstruct{Astruct{}Bstruct{}}funcmain(){varsSfmt.Println(unsafe.Sizeof(s))}

其最终输出结果也是0,完美切合人们对占位符的基本诉求,就是占着坑位,满足基本输入输出就好。

但这时候问题又出现了,为什么只有空结构会有这种特殊待遇,其他类型又不行?

这是Go编译器在内存分配时做的优化项

//baseaddressforall0-byteallocationsvarzerobaseuintptrfuncmallocgc(sizeuintptr,typ*_type,needzerobool)unsafe.Pointer{...ifsize==0{returnunsafe.Pointer(&zerobase)}}

当发现size为0时,会直接返回变量zerobase的引用,该变量是所有0字节的基准地址,不占据任何宽度。

因此空结构体的广泛使用,是Go开发者们借助了这个小优化,达到了占位符的目的。

使用场景

了解清楚为什么空结构作为占位符使用的原因后,我们更进一步了解其真实的使用场景有哪些。

主要分为三块:

实现方法接收者。

实现集合类型。

实现空通道。

实现方法接收者

在业务场景下,我们需要将方法组合起来,代表其是一个”分组“的,便于后续拓展和维护。

但是如果我们使用:

typeTstringfunc(s*T)Call()

又似乎有点不大友好,因为作为一个字符串类型,其本身会占据定的空间。

这种时候我们会采用空结构体的方式,这样也便于未来针对该类型进行公共字段等的增加。如下:

typeTstruct{}func(s*T)Call(){fmt.Println("脑子进煎鱼了")}funcmain(){varsTs.Call()}

在该场景下,使用空结构体从*度来考量是最合适的,易拓展,省空间,最结构化。

另外你会发现,其实你在日常开发中下意识就已经这么做了,你可以理解为设计模式和日常生活相结合的另类案例。

实现集合类型

在Go语言的标准库中并没有提供集合(Set)的相关实现,因此一般在代码中我们图方便,会直接用map来替代。

但有个问题,就是集合类型的使用,只需要用到key(键),不需要value(值)。

这就是空结构体大战身手的场景了:

//Sizeoftakesanexpressionxofanytypeandreturnsthesizeinbytes//ofahypotheticalvariablevasifvwasdeclaredviavarv=x.//Thesizedoesnotincludeanymemorypossiblyreferencedbyx.//Forinstance,ifxisaslice,Sizeofreturnsthesizeoftheslice//descriptor,notthesizeofthememoryreferencedbytheslice.//ThereturnvalueofSizeofisaGoconstant.funcSizeof(xArbitraryType)uintptr0

空结构体作为占位符,不会额外增加不必要的内存开销,很方便的就是解决了。

实现空通道

在Gochannel的使用场景中,常常会遇到通知型channel,其不需要发送任何数据,只是用于协调Goroutine的运行,用于流转各类状态或是控制并发情况。

如下:

funcmain(){%&&&&&%0gofunc(){time.Sleep(1*time.Second)close(ch)}()fmt.Println("脑子好像进...")<-chfmt.Println("煎鱼了!")}

输出结果:

//Sizeoftakesanexpressionxofanytypeandreturnsthesizeinbytes//ofahypotheticalvariablevasifvwasdeclaredviavarv=x.//Thesizedoesnotincludeanymemorypossiblyreferencedbyx.//Forinstance,ifxisaslice,Sizeofreturnsthesizeoftheslice//descriptor,notthesizeofthememoryreferencedbytheslice.//ThereturnvalueofSizeofisaGoconstant.funcSizeof(xArbitraryType)uintptr2

该程序会先输出”脑子好像进...“后,再睡眠一段时间再输出"煎鱼了!",达到间断控制channel的效果。

由于该channel使用的是空结构体,因此也不会带来额外的内存开销。

总结

在今天这篇文章中,给大家介绍了Go语言中几种常见类型的宽度,并且基于开头的问题”空结构体“进行了剖析。

最后分析了在业内代码最常见的三种模式,进入真实场景。不知道你以前是否有过类似本文的疑惑呢?

欢迎大家在评论区留言和交流:)

若有任何疑问欢迎评论区反馈和交流,最好的关系是互相成就,各位的点赞就是煎鱼创作的最大动力,感谢支持。

文章持续更新,可以微信搜【脑子进煎鱼了】阅读,本文GitHubgithub.com/eddycjy/blog已收录,欢迎Star催更。

声明声明:本网页内容为用户发布,旨在传播知识,不代表本网认同其观点,若有侵权等问题请及时与本网联系,我们将在第一时间删除处理。E-MAIL:11247931@qq.com
四万元没开发票税务局发多少钱 不给开发票这个情节要罚多少钱? 广东乌龙茶的种类 银行什么时候拉征信 600795国电电力,为什么在2010年4,5月突然从8块跌到4块呢?涨幅-50%... 学习很差怎么逆袭 高考文科差生五十天冲刺计划!!! ...漂亮女人,那些不大漂亮的还很善良有点丑的怎么办?她们不是很可怜吗... CS1.6 AWP经典的FRAG视频 求链接 ...大家谁有AWP的视频或者DEMO,要个看看,学学,谢谢!~ 原神钟离0命可以用吗 golang做数据分析(golang做大数据) Go语言map类型注意事项 原神钟离0命能玩吗_0命钟离强度分析 10-映射关系 map 原神钟离0命能玩吗 钟离几命质变 网络工程师的Golang之路 -- Go数据类型(Map、结构体) Golang并发map? Go语言入门看这篇就够了系列 - map 婴儿肠绞痛怎么抱不哭 炒虾尾怎么做更入味? 与男友分手后,他想与我做兄妹,是不是没爱过我 你认为交往三年的男友突然说做回兄妹吧,该如何是好? 帝舵哪个系列最经典 帝舵有哪些颜值高的手表推荐? 帝舵手表有哪些款式值得推荐? 保险柜的使用方法有哪些? 保险柜如何使用说明 黄鼻涕鼻塞是什么原因 鼻子不透气,有黄色鼻涕,没有其他的症状了,请问这是怎么回事?怎么办... Go语言学习(2)--map的底层原理 飞行模式是否可以收到信息 求成都永陵公园到新都中集大道的路线。不走二环路,明天车子限号。求解... 蜜蜂的特点是什么呢 宝宝急性喉炎发烧几天能好 宝宝急性喉炎一般几天恢复 喉炎该如何治疗? 婴儿得了急性喉炎会发烧吗 昂达v818 mini平板电脑,可以设置闹钟功能吗? 人体器官的名称,为何都带”月”字傍,也就是”月”字傍的由来? mac充电灯颜色代表什么意思啊? 《灵魂摆渡》揭秘赵吏的身世,两半灵魂双重人生,从人间到地狱 《灵魂摆渡》揭秘赵吏的身世 ,两半灵魂双重人生,从人间到地狱 Bosch|从天堂到地狱,咬着草莓,开始奇怪的周末狂欢 吃完毒蘑菇会有什么幻觉? 吃完毒蘑菇会有哪些幻觉? cpb的洗面奶性价比如何? cpb免税店和日本本土的区别? lol反甲和鬼书冲突吗 关于lol 的问题:1.反甲的被动能叠加么?2.装备了反甲和无尽,那被人攻击...