真实的国产乱ⅩXXX66竹夫人,五月香六月婷婷激情综合,亚洲日本VA一区二区三区,亚洲精品一区二区三区麻豆

成都創(chuàng)新互聯(lián)網(wǎng)站制作重慶分公司

Go語(yǔ)言之unsafe包之內(nèi)存布局

unsafe,顧名思義,是不安全的,Go定義這個(gè)包名也是這個(gè)意思,讓我們盡可能的不要使用它,如果你使用它,看到了這個(gè)名字,也會(huì)想到盡可能的不要使用它,或者更小心的使用它。

創(chuàng)新互聯(lián)公司專注于企業(yè)全網(wǎng)營(yíng)銷推廣、網(wǎng)站重做改版、連山網(wǎng)站定制設(shè)計(jì)、自適應(yīng)品牌網(wǎng)站建設(shè)、HTML5、商城網(wǎng)站制作、集團(tuán)公司官網(wǎng)建設(shè)、外貿(mào)網(wǎng)站制作、高端網(wǎng)站制作、響應(yīng)式網(wǎng)頁(yè)設(shè)計(jì)等建站業(yè)務(wù),價(jià)格優(yōu)惠性價(jià)比高,為連山等各大城市提供網(wǎng)站開(kāi)發(fā)制作服務(wù)。

雖然這個(gè)包不安全,但是它也有它的優(yōu)勢(shì),那就是可以繞過(guò)Go的內(nèi)存安全機(jī)制,直接對(duì)內(nèi)存進(jìn)行讀寫(xiě),所以有時(shí)候因?yàn)樾阅艿男枰瑫?huì)冒一些風(fēng)險(xiǎn)使用該包,對(duì)內(nèi)存進(jìn)行操作。

Sizeof函數(shù)

Sizeof函數(shù)可以返回一個(gè)類型所占用的內(nèi)存大小,這個(gè)大小只有類型有關(guān),和類型對(duì)應(yīng)的變量存儲(chǔ)的內(nèi)容大小無(wú)關(guān),比如bool型占用一個(gè)字節(jié)、int8也占用一個(gè)字節(jié)。

func main() {
    fmt.Println(unsafe.Sizeof(true))
    fmt.Println(unsafe.Sizeof(int8(0)))
    fmt.Println(unsafe.Sizeof(int16(10)))
    fmt.Println(unsafe.Sizeof(int32(10000000)))
    fmt.Println(unsafe.Sizeof(int64(10000000000000)))
    fmt.Println(unsafe.Sizeof(int(10000000000000000)))
}

對(duì)于整型來(lái)說(shuō),占用的字節(jié)數(shù)意味著這個(gè)類型存儲(chǔ)數(shù)字范圍的大小,比如int8占用一個(gè)字節(jié),也就是8bit,所以它可以存儲(chǔ)的大小范圍是-128~~127,也就是?2^(n-1)到2^(n-1)?1,n表示bit,int8表示8bit,int16表示16bit,其他以此類推。

對(duì)于和平臺(tái)有關(guān)的int類型,這個(gè)要看平臺(tái)是32位還是64位,會(huì)取最大的。比如我自己測(cè)試,以上輸出,會(huì)發(fā)現(xiàn)int和int64的大小是一樣的,因?yàn)槲业氖?4位平臺(tái)的電腦。

func Sizeof(x ArbitraryType) uintptr

以上是Sizeof的函數(shù)定義,它接收一個(gè)ArbitraryType類型的參數(shù),返回一個(gè)uintptr類型的值。這里的ArbitraryType不用關(guān)心,他只是一個(gè)占位符,為了文檔的考慮導(dǎo)出了該類型,但是一般不會(huì)使用它,我們只需要知道它表示任何類型,也就是我們這個(gè)函數(shù)可以接收任意類型的數(shù)據(jù)。

// ArbitraryType is here for the purposes of documentation only and is not actually// part of the unsafe package. It represents the type of an arbitrary Go expression.
type ArbitraryType int

Alignof 函數(shù)

Alignof返回一個(gè)類型的對(duì)齊值,也可以叫做對(duì)齊系數(shù)或者對(duì)齊倍數(shù)。對(duì)齊值是一個(gè)和內(nèi)存對(duì)齊有關(guān)的值,合理的內(nèi)存對(duì)齊可以提高內(nèi)存讀寫(xiě)的性能,關(guān)于內(nèi)存對(duì)齊的知識(shí)可以參考相關(guān)文檔,這里不展開(kāi)介紹。

func main() {
   var b bool    var i8 int8    var i16 int16    var i64 int64    var f32 float32    var s string    var m map[string]string    var p *int32    fmt.Println(unsafe.Alignof(b))    fmt.Println(unsafe.Alignof(i8))    fmt.Println(unsafe.Alignof(i16))    fmt.Println(unsafe.Alignof(i64))    fmt.Println(unsafe.Alignof(f32))    fmt.Println(unsafe.Alignof(s))    fmt.Println(unsafe.Alignof(m))    fmt.Println(unsafe.Alignof(p)) }

從以上例子的輸出,可以看到,對(duì)齊值一般是2^n,最大不會(huì)超過(guò)8(原因見(jiàn)下面的內(nèi)存對(duì)齊規(guī)則)。Alignof的函數(shù)定義和Sizeof基本上一樣。這里需要注意的是每個(gè)人的電腦運(yùn)行的結(jié)果可能不一樣,大同小異。

func Alignof(x ArbitraryType) uintptr

此外,獲取對(duì)齊值還可以使用反射包的函數(shù),也就是說(shuō):unsafe.Alignof(x)等價(jià)于reflect.TypeOf(x).Align()

Offsetof 函數(shù)

Offsetof函數(shù)只適用于struct結(jié)構(gòu)體中的字段相對(duì)于結(jié)構(gòu)體的內(nèi)存位置偏移量。結(jié)構(gòu)體的第一個(gè)字段的偏移量都是0.

func main() {
   var u1 user1    fmt.Println(unsafe.Offsetof(u1.b))    fmt.Println(unsafe.Offsetof(u1.i))    fmt.Println(unsafe.Offsetof(u1.j)) }
type user1 struct {    b byte    i int32    j int64
}

字段的偏移量,就是該字段在struct結(jié)構(gòu)體內(nèi)存布局中的起始位置(內(nèi)存位置索引從0開(kāi)始)。根據(jù)字段的偏移量,我們可以定位結(jié)構(gòu)體的字段,進(jìn)而可以讀寫(xiě)該結(jié)構(gòu)體的字段,哪怕他們是私有的,***的感覺(jué)有沒(méi)有。偏移量的概念,我們會(huì)在下一小結(jié)詳細(xì)介紹。

此外,unsafe.Offsetof(u1.i)等價(jià)于reflect.TypeOf(u1).Field(i).Offset

有意思的struct大小

我們定義一個(gè)struct,這個(gè)struct有3個(gè)字段,它們的類型有byte,int32以及int64,但是這三個(gè)字段的順序我們可以任意排列,那么根據(jù)順序的不同,一共有6種組合。

type user1 struct {
    b byte
    i int32
    j int64
}
type user2 struct {    b byte    j int64    i int32
}
type user3 struct {    i int32    b byte    j int64
}
type user4 struct {    i int32    j int64    b byte
}
type user5 struct {    j int64    b byte    i int32
}
type user6 struct {    j int64    i int32    b byte
}

根據(jù)這6種組合,定義了6個(gè)struct,分別位user1,user2,…,user6,那么現(xiàn)在大家猜測(cè)一下,這6種類型的struct占用的內(nèi)存是多少,就是unsafe.Sizeof()的值。

大家可能猜測(cè)1+4+8=13,因?yàn)閎yte的大小為1,int32大小為4,int64大小為8,而struct其實(shí)就是一個(gè)字段的組合,所以猜測(cè)struct大小為字段大小之和也很正常。

但是,但是,我可以明確的說(shuō),這是錯(cuò)誤的。

為什么是錯(cuò)誤的,因?yàn)橛袃?nèi)存對(duì)齊存在,編譯器使用了內(nèi)存對(duì)齊,那么最后的大小結(jié)果就不一樣了?,F(xiàn)在我們正式驗(yàn)證下,這幾種struct的值。

func main() {
   var u1 user1    
   var u2 user2    
   var u3 user3    
   var u4 user4    
   var u5 user5    
   var u6 user6    fmt.Println("u1 size is ",unsafe.Sizeof(u1))    fmt.Println("u2 size is ",unsafe.Sizeof(u2))    fmt.Println("u3 size is ",unsafe.Sizeof(u3))    fmt.Println("u4 size is ",unsafe.Sizeof(u4))    fmt.Println("u5 size is ",unsafe.Sizeof(u5))    fmt.Println("u6 size is ",unsafe.Sizeof(u6)) }

從以上輸出可以看到,結(jié)果是:

u1 size is  16
u2 size is  24
u3 size is  16
u4 size is  24
u5 size is  16
u6 size is  16

結(jié)果出來(lái)了(我的電腦的結(jié)果,Mac64位,你的可能不一樣),4個(gè)16字節(jié),2個(gè)24字節(jié),既不一樣,又不相同,這說(shuō)明:

  1. 內(nèi)存對(duì)齊影響struct的大小

  2. struct的字段順序影響struct的大小

綜合以上兩點(diǎn),我們可以得知,不同的字段順序,最終決定struct的內(nèi)存大小,所以有時(shí)候合理的字段順序可以減少內(nèi)存的開(kāi)銷。

內(nèi)存對(duì)齊會(huì)影響struct的內(nèi)存占用大小,現(xiàn)在我們就詳細(xì)分析下,為什么字段定義的順序不同會(huì)導(dǎo)致struct的內(nèi)存占用不一樣。

在分析之前,我們先看下內(nèi)存對(duì)齊的規(guī)則:

  1. 對(duì)于具體類型來(lái)說(shuō),對(duì)齊值=min(編譯器默認(rèn)對(duì)齊值,類型大小Sizeof長(zhǎng)度)。也就是在默認(rèn)設(shè)置的對(duì)齊值和類型的內(nèi)存占用大小之間,取最小值為該類型的對(duì)齊值。我的電腦默認(rèn)是8,所以最大值不會(huì)超過(guò)8.

  2. struct在每個(gè)字段都內(nèi)存對(duì)齊之后,其本身也要進(jìn)行對(duì)齊,對(duì)齊值=min(默認(rèn)對(duì)齊值,字段最大類型長(zhǎng)度)。這條也很好理解,struct的所有字段中,最大的那個(gè)類型的長(zhǎng)度以及默認(rèn)對(duì)齊值之間,取最小的那個(gè)。

以上這兩條規(guī)則要好好理解,理解明白了才可以分析下面的struct結(jié)構(gòu)體。在這里再次提醒,對(duì)齊值也叫對(duì)齊系數(shù)、對(duì)齊倍數(shù),對(duì)齊模數(shù)。這就是說(shuō),每個(gè)字段在內(nèi)存中的偏移量是對(duì)齊值的倍數(shù)即可

我們知道byte,int32,int64的對(duì)齊值分別為1,4,8,占用內(nèi)存大小也是1,4,8。那么對(duì)于第一個(gè)structuser1,它的字段順序是byte、int32、int64,我們先使用第1條內(nèi)存對(duì)齊規(guī)則進(jìn)行內(nèi)存對(duì)齊,其內(nèi)存結(jié)構(gòu)如下,內(nèi)存布局中有豎線(|),用于每四個(gè)字節(jié)的分割,下同。

bxxx|iiii|jjjj|jjjj

user1類型,第1個(gè)字段byte,對(duì)齊值1,大小1,所以放在內(nèi)存布局中的第1位。

第2個(gè)字段int32,對(duì)齊值4,大小4,所以它的內(nèi)存偏移值必須是4的倍數(shù),在當(dāng)前的user1中,就不能從第2位開(kāi)始了,必須從第5位開(kāi)始,也就是偏移量為4。第2,3,4位由編譯器進(jìn)行填充,一般為值0,也稱之為內(nèi)存空洞。所以第5位到第8位為第2個(gè)字段i。

第3字段,對(duì)齊值為8,大小也是8。因?yàn)?code>user1前兩個(gè)字段已經(jīng)排到了第8位,所以下一位的偏移量正好是8,是第3個(gè)字段對(duì)齊值的倍數(shù),不用填充,可以直接排列第3個(gè)字段,也就是從第9位到第16位為第3個(gè)字段j。

現(xiàn)在第一條內(nèi)存對(duì)齊規(guī)則后,內(nèi)存長(zhǎng)度已經(jīng)為16個(gè)字節(jié),我們開(kāi)始使用內(nèi)存的第2條規(guī)則進(jìn)行對(duì)齊。根據(jù)第二條規(guī)則,默認(rèn)對(duì)齊值8,字段中最大類型長(zhǎng)度也是8,所以求出結(jié)構(gòu)體的對(duì)齊值位8,我們目前的內(nèi)存長(zhǎng)度為16,是8的倍數(shù),已經(jīng)實(shí)現(xiàn)了對(duì)齊。

所以到此為止,結(jié)構(gòu)體user1的內(nèi)存占用大小為16字節(jié)。

現(xiàn)在我們?cè)俜治鲆粋€(gè)user2類型,它的大小是24,只是調(diào)換了一下字段i和j的順序,就多占用了8個(gè)字節(jié),我們看看為什么?還是先使用我們的內(nèi)存第1條規(guī)則分析。

bxxx|xxxx|jjjj|jjjj|iiii

按對(duì)齊值和其占用的大小,第1個(gè)字段b偏移量為0,占用1個(gè)字節(jié),放在第1位。

第2個(gè)字段j,是int64,對(duì)齊值和大小都是8,所以要從偏移量8開(kāi)始,也就是第9到16位為j,這也就意味著第2到8位被編譯器填充。

目前整個(gè)內(nèi)存布局已經(jīng)偏移了16位,正好是第3個(gè)字段i的對(duì)齊值4的倍數(shù),所以不用填充,可以直接排列,第17到20位為i。

現(xiàn)在所有字段對(duì)齊好了,整個(gè)內(nèi)存大小為1+7+8+4=20個(gè)字節(jié),我們開(kāi)始使用內(nèi)存對(duì)齊的第2條規(guī)則,也就是結(jié)構(gòu)體的對(duì)齊,通過(guò)默認(rèn)對(duì)齊值和最大的字段大小,求出結(jié)構(gòu)體的對(duì)齊值為8。

現(xiàn)在我們的整個(gè)內(nèi)存布局大小為20,不是8的倍數(shù),所以我們需要進(jìn)行內(nèi)存填充,補(bǔ)足到8的倍數(shù),最小的就是24,所以對(duì)齊后整個(gè)內(nèi)存布局為

bxxx|xxxx|jjjj|jjjj|iiii|xxxx

所以這也是為什么我們最終獲得的user2的大小為24的原因。
基于以上辦法,我們可以得出其他幾個(gè)struct的內(nèi)存布局。

user3

iiii|bxxx|jjjj|jjjj

user4

iiii|xxxx|jjjj|jjjj|bxxx|xxxx

user5

jjjj|jjjj|bxxx|iiii

user6

jjjj|jjjj|iiii|bxxx

以上給出了答案,推到過(guò)程大家可以參考user1user2試試。


文章標(biāo)題:Go語(yǔ)言之unsafe包之內(nèi)存布局
文章源于:http://weahome.cn/article/jhdjgi.html

其他資訊

在線咨詢

微信咨詢

電話咨詢

028-86922220(工作日)

18980820575(7×24)

提交需求

返回頂部