<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          Go 空結構體引發(fā)的大型打臉現場

          共 3361字,需瀏覽 7分鐘

           ·

          2021-03-18 22:19

          背景

          上周讀者問了我一道題,覺得挺有意義的,就在這里分享一下,我們先來看一下這個題:

          type User struct {
          }

          func FPrint(u User) {
           fmt.Printf("FPrint %p\n", &u)
          }

          func main() {
           u := User{}
           FPrint(u)
           fmt.Printf("main: %p\n", &u)
          }
          // 運行結果
          FPrint 0x118eff0
          main: 0x118eff0

          看了運行結果,大多數朋友應該和我一樣,一臉懵逼?Go語言不是只有值傳遞嘛?之前我還寫過一篇關于"Go語言參數傳遞是傳值還是傳引用嗎?",已經得出明確的結論,Go語言的確是只有值傳遞,這不是打臉了嘛。。。

          既然已經出現了這樣的結果,那么就要給出一個合理的解釋,不要再讓氣氛尷尬下去,于是我給出了我的猜想,如下:

          • 猜想一:這是一個bug
          • 猜想二:結構體的特殊特性導致的

          猜想一有點天馬行空的感覺,暫時也無法驗證,所以我們先來驗證猜想二,請開始我的表演,都坐下,我要裝逼了。。。。

          驗證猜想二:結構體的特殊特性導致的

          上面的那道題中傳參是一個空結構體,如果改成一個帶字段的結構體會是什么樣呢?我們來看一下:

          type UserIsEmpty struct {
          }

          type UserHasField struct {
           Age uint64 `json:"age"`
          }

          func FPrint(uIsEmpty UserIsEmpty, uHasField UserHasField) {
           fmt.Printf("FPrint uIsEmpty:%p uHasField:%p\n", &uIsEmpty, &uHasField)
          }

          func main() {
           uIsEmpty := UserIsEmpty{}
           uHasField := UserHasField{
            Age: 10,
           }
           FPrint(uIsEmpty, uHasField)
           fmt.Printf("main: uIsEmpty:%p uHasField:%p\n", &uIsEmpty, &uHasField)
          }
          // 運行結果:
          FPrint uIsEmpty:0x118fff0 uHasField:0xc0000ba008
          main: uIsEmpty:0x118fff0 uHasField:0xc0000ba000

          從結果我們可以看出來,帶字段的結構體確實是值傳遞,那么就證明空結構體有貓膩,有進展了,帶著這個線索,我們來看一看這段代碼的匯編部分,執(zhí)行go tool compile -N -l -S test.go,可以得到匯編部分,截取重要部分:

          從結果上我們看到有調用runtime.newobject(SB)來進行分配內存,順著這個在runtme/malloc.go中找到了他的實現:

          func newobject(typ *_type) unsafe.Pointer {
           return mallocgc(typ.size, typ, true)
          }

          newobject()中主要是調用了mallocgc()方法,在這里我找到了答案。因為mallocgc()代碼比較長,這里我截取關鍵部分:

          func mallocgc(size uintptr, typ *_type, needzero bool) unsafe.Pointer {
           if gcphase == _GCmarktermination {
            throw("mallocgc called with gcphase == _GCmarktermination")
           }

           if size == 0 {
            return unsafe.Pointer(&zerobase)
           }
          ..........
          }

          如果 size0 的時候,統(tǒng)一返回的都是全局變量 zerobase 的地址。到這里可能還會有一些伙伴有疑惑,這個跟上面的題有什么關系?那是因為你還不知道一個知識點:正常struct是占用一小塊內存的,并且結構體的大小是要經過邊界,長度的對齊的,但是“空結構體”是不占內存的,size0?,F在一切都可以說的清了,總結原因:

          因為空結構體是不占用內存的,所以size為0,在內存分配時,size0會統(tǒng)一返回zerobase的地址,所以空結構體在進行參數傳遞時,發(fā)生值拷貝后地址都是一樣的,才造成了這個質疑Go不是值傳遞的假象。

          空結構體特性延伸

          既然說到了空結構體,就在這里補充一個關于空結構體的知識點:空結構體做為結構體內置字段時是否進行內存對齊。

          先來看一個例子:

          func main()
           fmt.Println(unsafe.Sizeof(Test1{}))
           fmt.Println(unsafe.Sizeof(Test2{}))
           fmt.Println(unsafe.Sizeof(Test3{}))

          }

          type Test1 struct {
           s struct{}
           n byte
           m byte
          }

          type Test2 struct {
           n byte
           s struct{}
           c byte
          }

          type Test3 struct {
           b byte
           s struct{}
          }
          //運行結果
          2
          2
          2

          根據運行結果我們可以得出結論:

          空結構體在結構體中的前面和中間時,是不占用空間的,但是當空結構體放到結構體中的最后時,會進行特殊填充,struct { } 作為最后一個字段,會被填充對齊到前一個字段的大小,地址偏移對齊規(guī)則不變;

          總結

          最后做一個全文總結吧:

          1. 空結構體也是一個結構體,不過他的size為0,所有的空結構體內存分配都是同一個地址,都是zerobase的地址;
          2. 空結構體作為內嵌字段時要注意放置的順序,當作為最后一個字段時會進行特殊填充,會被填充對齊到前一個字段的大小,地址偏移對齊規(guī)則不變;


          推薦閱讀


          福利

          我為大家整理了一份從入門到進階的Go學習資料禮包,包含學習建議:入門看什么,進階看什么。關注公眾號 「polarisxu」,回復 ebook 獲??;還可以回復「進群」,和數萬 Gopher 交流學習。

          瀏覽 75
          點贊
          評論
          收藏
          分享

          手機掃一掃分享

          分享
          舉報
          評論
          圖片
          表情
          推薦
          點贊
          評論
          收藏
          分享

          手機掃一掃分享

          分享
          舉報
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  久久精品福利免费视频 | 中国一级免费黄色片 | 亚洲中文无码av 亚洲专区在线播放 | 一区二区三区四区久久 | 毛片软件 |