網站首頁 編程語言 正文
當我們想要在 Go 語言中初始化一個結構時,其實會使用到兩個完全不同的關鍵字,也就是?make
?和?new
,同時出現兩個用于『初始化』的關鍵字對于初學者來說可能會感到非常困惑,不過它們兩者有著卻完全不同的作用。
在 Go 語言中,make
?關鍵字的主要作用是初始化內置的數據結構,也就是我們在前面提到的 數組和切片、哈希表 和 Channel,而當我們想要獲取指向某個類型的指針時其實可以使用?new
?關鍵字,只是知道如何使用?new
?的人真的比較少,我們在這一節中就會介紹?make
?和?new
?它們的區別以及實現原理。
概述
雖然?make
?和?new
??都是能夠用于初始化數據結構,但是它們兩者能夠初始化的結構類型卻有著較大的不同;make
?在 Go 語言中只能用于初始化語言中的基本類型:
slice := make([]int, 0, 100) hash := make(map[int]bool, 10) ch := make(chan int, 5)
這些基本類型都是語言為我們提供的,我們在前面的章節中其實已經介紹過了它們初始化的過程以及原理,但是在這里還是需要提醒各位讀者注意的是,這三者返回了不同類型的數據結構:
-
slice
?是一個包含?data
、cap
?和?len
?的結構體; -
hash
?是一個指向?hmap
?結構體的指針; -
ch
?是一個指向?hchan
?結構體的指針;
而另一個用于初始化數據結構的關鍵字?new
?的作用其實就非常簡單了,它只是接收一個類型作為參數然后返回一個指向這個類型的指針:
i := new(int) var v int i := &v
上述代碼片段中的兩種不同初始化方法其實是等價的,它們都會創建一個指向?int
?零值的指針。
到了這里我們對 Go 語言中這兩種不同關鍵字的使用也有了一定的了解:make
?用于創建切片、哈希表和管道等內置數據結構,new
?用于分配并創建一個指向對應類型的指針。
實現原理
接下來我們將分別介紹?make
?和?new
?在初始化不同數據結構時的具體過程,我們會從編譯期間和運行時兩個不同的階段理解這兩個關鍵字的原理,不過由于前面已經詳細地介紹過?make
?的實現原理,所以我們會將重點放在?new
?上從 Go 語言的源代碼層面分析它的實現。
make
在前面的章節中我們其實已經談到過?make
?在創建 數組和切片、哈希表 和 Channel 的具體過程,所以在這一小節中,我們也只是會簡單提及?make
?相關的數據結構初始化原理。
在編譯期間的 類型檢查 階段,Go 語言其實就將代表?make
?關鍵字的?OMAKE
?節點根據參數類型的不同轉換成了?OMAKESLICE
、OMAKEMAP
?和?OMAKECHAN
?三種不同類型的節點,這些節點最終也會調用不同的運行時函數來初始化數據結構。
new
內置函數?new
?會在編譯期間的 SSA 代碼生成 階段經過?callnew
?函數的處理,如果請求創建的類型大小時 0,那么就會返回一個表示空指針的?zerobase
?變量,在遇到其他情況時會將關鍵字轉換成?newobject
:
func callnew(t *types.Type) *Node { if t.NotInHeap() { yyerror("%v is go:notinheap; heap allocation disallowed", t) } dowidth(t) if t.Size() == 0 { z := newname(Runtimepkg.Lookup("zerobase")) z.SetClass(PEXTERN) z.Type = t return typecheck(nod(OADDR, z, nil), ctxExpr) } fn := syslook("newobject") fn = substArgTypes(fn, t) v := mkcall1(fn, types.NewPtr(t), nil, typename(t)) v.SetNonNil(true) return v }
需要提到的是,哪怕當前變量是使用?var
?進行初始化,在這一階段可能會被轉換成?newobject
?的函數調用并在堆上申請內存:
func walkstmt(n *Node) *Node { switch n.Op { case ODCL: v := n.Left if v.Class() == PAUTOHEAP { if prealloc[v] == nil { prealloc[v] = callnew(v.Type) } nn := nod(OAS, v.Name.Param.Heapaddr, prealloc[v]) nn.SetColas(true) nn = typecheck(nn, ctxStmt) return walkstmt(nn) } case ONEW: if n.Esc == EscNone { r := temp(n.Type.Elem()) r = nod(OAS, r, nil) r = typecheck(r, ctxStmt) init.Append(r) r = nod(OADDR, r.Left, nil) r = typecheck(r, ctxExpr) n = r } else { n = callnew(n.Type.Elem()) } } }
當然這也不是絕對的,如果當前聲明的變量或者參數不需要在當前作用域外『生存』,那么其實就不會被初始化在堆上,而是會初始化在當前函數的棧中并隨著 函數調用 的結束而被銷毀。
newobject
?函數的工作就是獲取傳入類型的大小并調用?mallocgc
?在堆上申請一片大小合適的內存空間并返回指向這片內存空間的指針:
func newobject(typ *_type) unsafe.Pointer { return mallocgc(typ.size, typ, true) }
mallocgc
?函數的實現大概有 200 多行代碼,在這一節中就不展開詳細分析了,我們會在后面的章節中詳細介紹 Go 語言的內存管理機制。
總結
到了最后,簡單總結一下 Go 語言中?make
?和?new
?關鍵字的實現原理,make
?關鍵字的主要作用是創建切片、哈希表和 Channel 等內置的數據結構,而?new
?的主要作用是為類型申請一片內存空間,并返回指向這片內存的指針。
Reference
- Allocation with new
- Allocation with make
- Make and new
原文鏈接:https://mp.weixin.qq.com/s/6gDoUblSUO05Lu1kNV9FIg
- 上一篇:沒有了
- 下一篇:沒有了
相關推薦
- 2022-10-21 C++調用matlab函數的實例_C 語言
- 2022-08-27 pytest多文件執行順序控制詳解_python
- 2024-03-09 【Redis】Redis中的布隆過濾器
- 2023-05-10 Python中的SOLID原則實例詳解_python
- 2023-01-10 Flutter?SystemChrome使用方法詳解_Android
- 2023-05-31 pandas.DataFrame的for循環迭代的實現_python
- 2022-04-25 Entity?Framework?Core生成列并跟蹤列記錄_實用技巧
- 2022-11-07 Python實現四舍五入的兩個方法總結_python
- 欄目分類
-
- 最近更新
-
- window11 系統安裝 yarn
- 超詳細win安裝深度學習環境2025年最新版(
- Linux 中運行的top命令 怎么退出?
- MySQL 中decimal 的用法? 存儲小
- get 、set 、toString 方法的使
- @Resource和 @Autowired注解
- Java基礎操作-- 運算符,流程控制 Flo
- 1. Int 和Integer 的區別,Jav
- spring @retryable不生效的一種
- Spring Security之認證信息的處理
- Spring Security之認證過濾器
- Spring Security概述快速入門
- Spring Security之配置體系
- 【SpringBoot】SpringCache
- Spring Security之基于方法配置權
- redisson分布式鎖中waittime的設
- maven:解決release錯誤:Artif
- restTemplate使用總結
- Spring Security之安全異常處理
- MybatisPlus優雅實現加密?
- Spring ioc容器與Bean的生命周期。
- 【探索SpringCloud】服務發現-Nac
- Spring Security之基于HttpR
- Redis 底層數據結構-簡單動態字符串(SD
- arthas操作spring被代理目標對象命令
- Spring中的單例模式應用詳解
- 聊聊消息隊列,發送消息的4種方式
- bootspring第三方資源配置管理
- GIT同步修改后的遠程分支