[譯]空結構體
這篇文章探討了我喜歡的Go資料型別,空結構體。
空結構體是一個沒有field的結構體型別。這裡有幾個例子,有命名和匿名形式:
type Q struct{}
var q struct{}
所以,如果空結構體沒有成員,我們該怎麼使用它?
width
在深入研究空結構體本身前,我想簡要討論下width
。
術語width來自於gc編譯器,儘管它的詞源可能追溯到幾十年年。
width描述了型別例項佔用的位元組數目。因為一個程式的地址空間是一維的,我認為witdh比size更合適。
width一個型別的屬性。因為Go程式的每個值都有一個型別,值型別定義了它的witdh,一般是8位元的倍數。
我們可以發現任何值的寬度,它的型別的width使用unsafe.Sizeof()函式:
var s string
var c complex128
fmt.Println(unsafe.Sizeof(s)) // prints 8
fmt.Println(unsafe.Sizeof(c)) // prints 16
http://play.golang.org/p/4mzdOKW6uQ
陣列型別的width是它的元素型別的倍數:
var a [3]uint32
fmt.Println(unsafe.Sizeof(a)) // prints 12
http://play.golang.org/p/YC97xsGG73
結構體提供了更靈活的方式來定義組合型別,它的width是所有組成型別的width的總和,加上padding:
type S struct {
a uint16
b uint32
}
var s S
fmt.Println(unsafe.Sizeof(s)) // prints 8, not 6
上面的例子演示了padding的一方面,值必須在記憶體中對齊為它的width的倍數。在這個場景中,在a和b中間被編譯器加入了2個位元組的padding。
更新:Russ Cox已經解釋了width和對齊無關。你可以閱讀下面的評論。
空型別
現在,我們已經探討了width,很明顯空型別的width是零。它佔用了零位元組的儲存空間:
var s struct{}
fmt.Println(unsafe.Sizeof(s)) // prints 0
因為空型別佔用了零位元組,所以它不需要填充。這樣,空結構體組成的一個結構體也不佔用儲存空間:
type S struct {
A struct{}
B struct{}
}
var s S
fmt.Println(unsafe.Sizeof(s)) // prints 0
http://play.golang.org/p/PyGYFmPmMt
我們可以用空型別做什麼
適用於Go語言的正交性,空型別和其他型別一樣,是一個結構型別。你所使用的正常的結構體的所有的屬性適用於空的結構。
你可以宣告一個結構體陣列struct{}s,但是他們當然不會佔用儲存空間:
var x [1000000000]struct{}
fmt.Println(unsafe.Sizeof(x)) // prints 0
http://play.golang.org/p/0lWjhSQmkc
struct{}s的切片僅僅消耗他們的slice頭的空間。就像上面演示的那樣,他們的後端陣列不消耗空間:
var x = make([]struct{}, 1000000000)
fmt.Println(unsafe.Sizeof(x)) // prints 12 in the playground
http://play.golang.org/p/vBKP8VQpd8
當然,正常的子切片,內建的len和cap和預期一樣工作:
var x = make([]struct{}, 100)
var y = x[:50]
fmt.Println(len(y), cap(y)) // prints 50 100
http://play.golang.org/p/8cO4SbrWVP
你可以獲取struct{}值的地址,當它的可以地址化,就像其他值一樣:
var a struct{}
var b = &a
有意思的是,兩個struct{}值的地址可能是相同的:
var a, b struct{}
fmt.Println(&a == &b) // true
http://play.golang.org/p/uMjQpOOkX1
對於[]struct{}s,這個屬性也是可見的:
a := make([]struct{}, 10)
b := make([]struct{}, 20)
fmt.Println(&a == &b) // false, a and b are different slices
fmt.Println(&a[0] == &b[0]) // true, their backing arrays are the same
http://play.golang.org/p/oehdExdd96
為什麼是這樣?如果你考慮一下,空結構體不包含成員,所以可以不包含資料。如果空結構體不包含資料,不能決定是否兩個struct{}值是否是相同的。它們在效果上,是可替代的。
a := struct{}{} // not the zero value, a real new struct{} instance
b := struct{}{}
fmt.Println(a == b) // true
http://play.golang.org/p/K9qjnPiwM8
注意:這個屬性不是spec所需要的,但是注意:Two distinct zero-size variables may have the same address in memory.
struct{} 作為 method receiver
現在,我們已經演示了空結構體有任何其他型別一樣的行為,因此,我們可以把它們作為函式接收者來使用:
type S struct{}
func (s *S) addr() { fmt.Printf("%p\n", s) }
func main() {
var a, b S
a.addr() // 0x1beeb0
b.addr() // 0x1beeb0
}
http://play.golang.org/p/YSQCczP-Pt
在這個例子中,展示了all zero sized值的地址為0x1beeb0。精確的地址可能因Go的版本而不同。
封裝
謝謝你的閱讀。本文已接近800字,比預期更多,我還有更多的計劃。
儘管本文關注於語言黑盒,有一個空結構體重要的實際用途。chan struct{}用來在不同的go routine之間傳送訊號。
翻譯
更新:Damian Gryski指出我忽略了 Brad Fitzpatrick的iter包。我留下它作為讀者的練習來探索Brad的深遠影響的貢獻。
相關文章
- Struct composition with Go
- Friday pop quiz: the smallest buffer
- Constant errors
- Stupid Go declaration tricks
如果我翻譯得不對,請幫我改善: https://github.com/itfanr/articles-about-golang/blob/master/2016-10/2.the-empty-struct.md
相關文章
- 【譯】改善結構體相等性效能結構體
- [譯] part 16: golang 結構體structuresGolang結構體Struct
- JVM元空間Metaspace的記憶體結構JVM記憶體
- C語言-->(十四)結構體、巨集、編譯C語言結構體編譯
- 詳解 Go 空結構體的 3 種使用場景Go結構體
- 妙啊,空結構體還能這麼用?Go語言的結構體看這篇就夠了結構體Go
- 結構體中套用其他_結構體結構體
- innodb表空間儲存結構
- 條件編譯、多檔案程式設計、結構體編譯程式設計結構體
- 結構體結構體
- [譯] Golang 資料結構:樹Golang資料結構
- 編譯deno,deno結構解析編譯
- 【TABLESPACE】Oracle 表空間結構說明Oracle
- 空間劃分的資料結構資料結構
- Go 結構體Go結構體
- 結構體與共用體結構體
- 3:Oracle體系結構(邏輯結構)Oracle
- 【公益譯文】美國網路空間組織架構架構
- 已知結構體成員地址獲取結構體首地址結構體
- 【PG體系結構】PG體系結構簡單說明
- 16-結構體結構體
- 結構體學習結構體
- FFmpeg結構體:AVOutputFormat結構體ORM
- 結構體問題結構體
- BeanFactory體系結構Bean
- MongoDB 體系結構MongoDB
- JVM - 整體結構JVM
- 類與結構體結構體
- 記憶體結構記憶體
- Servlet 體系結構Servlet
- C 結構體概述結構體
- 結構體相關結構體
- Deployer整體結構
- MySQL 體系結構MySql
- 【JVM體系結構】JVM
- GPU硬體結構GPU
- 結構體的大小結構體
- [譯]從磁碟結構到B+樹
- Postgresql資料庫體系結構-程式和記憶體結構SQL資料庫記憶體