一文帶你感受Go語言空結(jié)構(gòu)體的魔力
前言
在 Go 語言中,有一種特殊的用法可能讓許多人感到困惑,那就是空結(jié)構(gòu)體 struct{}。在本文中,我將對 Go 空結(jié)構(gòu)體進(jìn)行詳解,準(zhǔn)備好了嗎?準(zhǔn)備一杯你最喜歡的飲料或茶,隨著本文一探究竟吧。
什么是空結(jié)構(gòu)體
不包含任何字段的結(jié)構(gòu)體,就是空結(jié)構(gòu)體。它有以下兩種定義方式:
匿名空結(jié)構(gòu)體
var e sruct{}命名空結(jié)構(gòu)體
type EmptyStruct struct{}
var e EmptyStruct空結(jié)構(gòu)體的特點(diǎn)
空結(jié)構(gòu)體主要有以下幾個(gè)特點(diǎn):
- 零內(nèi)存占用
- 地址相同
- 無狀態(tài)
零內(nèi)存占用
空結(jié)構(gòu)體不占用任何內(nèi)存空間,這使得空結(jié)構(gòu)體在內(nèi)存優(yōu)化方面非常有用,我們來通過例子看看是否真的是零內(nèi)存占用:
package main
import (
"fmt"
"unsafe"
)
func main() {
var a int
var b string
var e struct{}
fmt.Println(unsafe.Sizeof(a)) // 4
fmt.Println(unsafe.Sizeof(b)) // 8
fmt.Println(unsafe.Sizeof(e)) // 0
}
通過打印結(jié)果對比可知,空結(jié)構(gòu)體內(nèi)存占用為 0。
地址相同
無論創(chuàng)建多少個(gè)空結(jié)構(gòu)體,它們所指向的地址都相同的。
package main
import (
"fmt"
)
func main() {
var e struct{}
var e2 struct{}
fmt.Printf("%p\n", &e) // 0x90b418
fmt.Printf("%p\n", &e2) // 0x90b418
fmt.Println(&e == &e2) // true
}
無狀態(tài)
由于空結(jié)構(gòu)體不包含任何字段,因此它不能有狀態(tài)。這使得空結(jié)構(gòu)體在表示無狀態(tài)的對象或情況時(shí)非常有用。
為什么是零內(nèi)存和地址相同
要理解為什么空結(jié)構(gòu)體在內(nèi)存上是零大?。銉?nèi)存)并且多個(gè)空結(jié)構(gòu)體的地址是相同的,需要深入研究 Go 的源碼。
/go/src/runtime/malloc.go
// base address for all 0-byte allocations
var zerobase uintptr
func mallocgc(size uintptr, typ *_type, needzero bool) unsafe.Pointer {
······
if size == 0 {
return unsafe.Pointer(&zerobase)
}
······根據(jù) malloc.go 源碼的部分內(nèi)容,當(dāng)要分配的對象大小 size 為 0 時(shí),會(huì)返回指向 zerobase 的指針。zerobase 是一個(gè)用于分配零字節(jié)對象的基準(zhǔn)地址,它不占用任何實(shí)際的內(nèi)存空間。
空結(jié)構(gòu)體的使用場景
空結(jié)構(gòu)體主要有以下三種使用場景:
- 實(shí)現(xiàn)
Set集合類型 - 用于通道信號(hào)
- 作為方法接收器
實(shí)現(xiàn) Set 集合類型
在 Go 語言中,雖然沒有內(nèi)置 Set 集合類型,但是我們可以利用 map 類型來實(shí)現(xiàn)一個(gè) Set 集合。由于 map 的 key 具有唯一性,我們可以將元素存儲(chǔ)為 key,而 value 沒有實(shí)際作用,為了節(jié)省內(nèi)存,我們可以使用空結(jié)構(gòu)體作為 value 的值。
package main
import"fmt"
type Set[K comparable] map[K]struct{}
func (s Set[K]) Add(val K) {
s[val] = struct{}{}
}
func (s Set[K]) Remove(val K) {
delete(s, val)
}
func (s Set[K]) Contains(val K) bool {
_, ok := s[val]
return ok
}
func main() {
set := Set[string]{}
set.Add("陳明勇")
fmt.Println(set.Contains("陳明勇")) // true
set.Remove("陳明勇")
fmt.Println(set.Contains("陳明勇")) // false
}用于通道信號(hào)
空結(jié)構(gòu)體常用于 Goroutine 之間的信號(hào)傳遞,尤其是不關(guān)心通道中傳遞的具體數(shù)據(jù),只需要一個(gè)觸發(fā)信號(hào)時(shí)。例如,我們可以使用空結(jié)構(gòu)體通道來通知一個(gè) Goroutine 停止工作:
package main
import (
"fmt"
"time"
)
func main() {
quit := make(chanstruct{})
gofunc() {
// 模擬工作
fmt.Println("工作中...")
time.Sleep(3 * time.Second)
// 關(guān)閉退出信號(hào)
close(quit)
}()
// 阻塞,等待退出信號(hào)被關(guān)閉
<-quit
fmt.Println("已收到退出信號(hào),退出中...")
}在這個(gè)例子中,創(chuàng)建了一個(gè)通道 quit,并在一個(gè)單獨(dú)的 Goroutine 中模擬執(zhí)行工作。在完成工作后,關(guān)閉了 quit 通道,表示退出信號(hào)。主函數(shù)在 <-quit 處阻塞,直到收到退出信號(hào),然后打印一條消息并退出程序。
由于通道使用的類型是空結(jié)構(gòu)體,因此不會(huì)帶來額外的內(nèi)存開銷。
在 Go 標(biāo)準(zhǔn)庫中,context 包中的 Context 接口的 Done() 方法返回一個(gè)通道信號(hào),用于通知相關(guān)操作的完成狀態(tài)。這個(gè)通道信號(hào)的返回值就是使用了空結(jié)構(gòu)體。
type Context interface {
Deadline() (deadline time.Time, ok bool)
Done() <-chanstruct{}
Err() error
Value(key any) any
}作為方法接收器
有時(shí)候我們需要?jiǎng)?chuàng)建一組方法集的實(shí)現(xiàn)(一般來說是實(shí)現(xiàn)一個(gè)接口),但并不需要在這個(gè)實(shí)現(xiàn)中存儲(chǔ)任何數(shù)據(jù),這種情況下,我們可以使用空結(jié)構(gòu)體來實(shí)現(xiàn):
type Person interface {
SayHello()
Sleep()
}
type CMY struct{}
func (c CMY) SayHello() {
fmt.Println("你好,我叫陳明勇。")
}
func (c CMY) Sleep() {
fmt.Println("陳明勇睡覺中...")
}這個(gè)例子定義了一個(gè)接口 Person 和一個(gè)結(jié)構(gòu)體 CMY ,并為 CMY 實(shí)現(xiàn)了 Person 接口,定義了一組方法(SayHello 和 Sleep)。
由于 CMY 結(jié)構(gòu)體為空結(jié)構(gòu)體,因此不會(huì)帶來額外的內(nèi)存開銷。
小結(jié)
在本文中,首先介紹了 Go 語言 空結(jié)構(gòu)體 的概念和定義方式,它有兩種定義方式;
隨后對 空結(jié)構(gòu)體 的特點(diǎn)進(jìn)行介紹,包括其零內(nèi)存和多個(gè)變量地址相同的特性;
接著進(jìn)一步深入源碼,探究了為什么空結(jié)構(gòu)體在 Go 語言中是零內(nèi)存且多變量地址相同,原因是當(dāng)要分配的對象大小 size 為 0 時(shí),會(huì)返回指向 zerobase 的指針;
最后列舉了空結(jié)構(gòu)體的三個(gè)使用場景,通過這些代碼示例,展示了空結(jié)構(gòu)體在實(shí)際應(yīng)用中的一些常見用途。
到此這篇關(guān)于一文帶你感受Go語言空結(jié)構(gòu)體的魔力的文章就介紹到這了,更多相關(guān)Go語言空結(jié)構(gòu)體內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
golang讀取yaml配置文件的方法實(shí)現(xiàn)
本文主要介紹了golang讀取yaml配置文件的方法實(shí)現(xiàn),文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2024-10-10
詳解Golang如何優(yōu)雅的終止一個(gè)服務(wù)
后端服務(wù)通常會(huì)需要?jiǎng)?chuàng)建子協(xié)程來進(jìn)行相應(yīng)的作業(yè),但進(jìn)程接受到終止信號(hào)或正常結(jié)束時(shí),并沒有判斷或等待子協(xié)程執(zhí)行結(jié)束,下面這篇文章主要給大家介紹了關(guān)于Golang如何優(yōu)雅的終止一個(gè)服務(wù)的相關(guān)資料,需要的朋友可以參考下2022-03-03
Web框架Gin中間件實(shí)現(xiàn)原理步驟解析
這篇文章主要為大家介紹了Web框架Gin中間件實(shí)現(xiàn)原理步驟解析,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-10-10
Golang使用反射的動(dòng)態(tài)方法調(diào)用詳解
Go是一種靜態(tài)類型的語言,提供了大量的安全性和性能。這篇文章主要和大家介紹一下Golang使用反射的動(dòng)態(tài)方法調(diào)用,感興趣的小伙伴可以了解一下2023-03-03
一文帶你了解Go語言標(biāo)準(zhǔn)庫strings的常用函數(shù)和方法
strings?庫包含了許多高效的字符串常用操作的函數(shù)和方法,巧用這些函數(shù)與方法,能極大的提高我們程序的性能。本文就來和大家分享一下Go標(biāo)準(zhǔn)庫strings的常用函數(shù)和方法,希望對大家有所幫助2022-11-11

