Go語言中new與make的使用解讀
在 Go 語言中,new 和 make 是兩個用于內(nèi)存分配的內(nèi)置函數(shù),但它們的作用和使用場景有顯著區(qū)別。
理解它們的核心在于:
new(T): 為類型T分配內(nèi)存,并將其初始化為零值,然后返回一個指向該內(nèi)存的指針 (*T)。make(T, args...): 僅用于 slice、map 和 channel 這三種引用類型的創(chuàng)建和初始化。它返回的是類型T本身(而不是指針*T),并且這個T已經(jīng)被正確初始化,可以直接使用。
下面詳細解釋:
new
作用:
- 分配內(nèi)存。
- 將分配的內(nèi)存清零(設(shè)置為相應(yīng)類型的零值)。
- 返回一個指向新分配的零值
T的指針 (*T)。
語法:
p := new(T)
其中 T 可以是任何類型。
返回值:
- 一個指向類型
T的指針 (*T)。
適用類型:
- 幾乎所有類型,包括基本類型(int, string, bool等)、結(jié)構(gòu)體(struct)、數(shù)組(array)等。
- 也可以用于 slice, map, channel,但通常不這么用,因為
new返回的是指向這些引用類型本身的指針,而引用類型本身在未初始化時是nil。
示例:
// 為 int 分配內(nèi)存,i 是 *int 類型,*i 的值是 0
var i *int = new(int)
fmt.Printf("i: %p, *i: %d\n", i, *i) // *i 是 0
// 為自定義結(jié)構(gòu)體 MyStruct 分配內(nèi)存,s 是 *MyStruct 類型
type MyStruct struct {
Field1 int
Field2 string
}
var s *MyStruct = new(MyStruct)
fmt.Printf("s: %p, s.Field1: %d, s.Field2: '%s'\n", s, s.Field1, s.Field2) // s.Field1 是 0, s.Field2 是 ""
// 對于引用類型(不推薦,但可以這么做)
var sl *[]int = new([]int) // sl 是 *[]int 類型,*sl 的值是 nil
fmt.Printf("sl: %p, *sl == nil: %t\n", sl, *sl == nil) // *sl 是 nil,無法直接使用 append 等操作
var m *map[string]int = new(map[string]int) // m 是 *map[string]int 類型,*m 的值是 nil
fmt.Printf("m: %p, *m == nil: %t\n", m, *m == nil) // *m 是 nil,無法直接賦值
make
作用:
- 僅用于 slice、map 和 channel 這三種內(nèi)置引用類型的創(chuàng)建。
- 它不僅分配內(nèi)存,還會初始化這些類型的內(nèi)部數(shù)據(jù)結(jié)構(gòu),使其立即可用。
語法:
- Slice:
make([]T, length, capacity)或make([]T, length)(capacity 默認(rèn)為 length) - Map:
make(map[K]V, initialCapacity)或make(map[K]V)(initialCapacity 可選) - Channel:
make(chan T, bufferCapacity)或make(chan T)(bufferCapacity 可選,默認(rèn)為0,即無緩沖)
返回值:
- 返回的是初始化后的類型
T本身(例如[]int,map[string]int,chan int),而不是指針。
適用類型:
- 只能是 slice, map, channel。
示例:
// 創(chuàng)建一個長度為 5,容量為 10 的 int 切片
s1 := make([]int, 5, 10)
fmt.Printf("s1: %v, len: %d, cap: %d\n", s1, len(s1), cap(s1)) // s1: [0 0 0 0 0], len: 5, cap: 10
s1[0] = 100 // 可以直接使用
// 創(chuàng)建一個初始容量(可選)的 map
m1 := make(map[string]int)
m1["age"] = 30 // 可以直接使用
fmt.Printf("m1: %v\n", m1)
// 創(chuàng)建一個帶緩沖的 channel
ch1 := make(chan int, 1)
ch1 <- 1 // 可以直接使用
fmt.Printf("Received from ch1: %d\n", <-ch1)
核心區(qū)別總結(jié)
| 特性 | new(T) | make(T, args...) |
|---|---|---|
| 目的 | 分配內(nèi)存,初始化為零值 | 創(chuàng)建并初始化 slice, map, channel 的內(nèi)部結(jié)構(gòu) |
| 返回類型 | 指針 *T | 類型 T 本身 (slice, map, or channel) |
| 適用類型 | 任何類型 | 僅 slice, map, channel |
| 初始化 | 內(nèi)存被清零 (zero value) | 初始化內(nèi)部數(shù)據(jù)結(jié)構(gòu),使其立即可用 (non-nil) |
| 常見用途 | 獲取指向零值變量的指針,尤其是結(jié)構(gòu)體指針 | 創(chuàng)建可用的 slice, map, channel |
為什么make只用于 slice, map, channel?
這三種類型在 Go 中是引用類型,它們內(nèi)部不僅僅是一塊簡單的內(nèi)存區(qū)域,還包含了更復(fù)雜的數(shù)據(jù)結(jié)構(gòu):
- Slice: 內(nèi)部包含指向底層數(shù)組的指針、長度(len)和容量(cap)。
make會分配底層數(shù)組并設(shè)置這些元數(shù)據(jù)。 - Map: 內(nèi)部通常是哈希表的實現(xiàn)。
make會初始化這個哈希表結(jié)構(gòu)。 - Channel: 內(nèi)部包含用于 goroutine 間同步和通信的機制,可能還有緩沖區(qū)。
make會設(shè)置這些。
如果對這些類型使用 new:
var s *[]int = new([]int) // *s 是一個 nil slice var m *map[string]int = new(map[string]int) // *m 是一個 nil map var c *chan int = new(chan int) // *c 是一個 nil channel
你會得到一個指向 nil slice/map/channel 的指針。這個 nil 狀態(tài)的引用類型是不能直接使用的(例如,不能向 nil map 添加鍵值對,不能向 nil slice append 元素,不能向 nil channel 發(fā)送數(shù)據(jù))。你需要先用 make 來初始化它們。
何時使用哪個?
使用 new(T):
當(dāng)你需要一個指向某個類型 T 的變量,并且希望它被初始化為其零值時。
最常見的場景是為結(jié)構(gòu)體分配內(nèi)存并獲取其指針,然后填充字段:
type Point struct{ X, Y int }
p := new(Point) // p 是 *Point,p.X 和 p.Y 都是 0
p.X = 10
這等價于:
var p Point // p 是 Point,p.X 和 p.Y 都是 0 ptr := &p // ptr 是 *Point ptr.X = 10
或者更簡潔的復(fù)合字面量:
p := &Point{} // p 是 *Point,p.X 和 p.Y 都是 0
p.X = 10
使用 make(T, args...):
- 當(dāng)你需要創(chuàng)建一個 slice、map 或 channel,并希望它們被正確初始化以便立即使用時。這是創(chuàng)建這三種類型的標(biāo)準(zhǔn)方式。
記住這個簡單的規(guī)則:
- 要創(chuàng)建 slice, map, channel,請使用
make。 - 要為其他類型(如 int, string, struct, array)分配內(nèi)存并獲取指向其零值的指針,請使用
new(或者更常見的,使用復(fù)合字面量&T{})。
總結(jié)
以上為個人經(jīng)驗,希望能給大家一個參考,也希望大家多多支持腳本之家。
相關(guān)文章
Go語言實現(xiàn)動態(tài)解析JSON數(shù)據(jù)的多種方式
本文主要介紹了Go語言實現(xiàn)動態(tài)解析JSON數(shù)據(jù)的多種方式,包括map[string]interface{}、interface{}、json.RawMessage及第三方庫gjson/mapstructure,具有一定的參考價值,感興趣的可以了解一下2025-05-05
golang Iris運行多個應(yīng)用的實現(xiàn)
本文主要介紹了golang Iris運行多個應(yīng)用的實現(xiàn),在Iris里面,提供了一種方式可以讓我們同時運行多個應(yīng)用,具有一定的參考價值,感興趣的可以了解一下2024-01-01
golang socket斷點續(xù)傳大文件的實現(xiàn)方法
今天小編就為大家分享一篇golang socket斷點續(xù)傳大文件的實現(xiàn)方法,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧2019-07-07
Go?iota關(guān)鍵字與枚舉類型實現(xiàn)原理
這篇文章主要介紹了Go?iota關(guān)鍵字與枚舉類型實現(xiàn)原理,iota是go語言的常量計數(shù)器,只能在常量的表達式中使用,更多相關(guān)內(nèi)容需要的小伙伴可以參考一下2022-07-07
go中Excelize處理excel表實現(xiàn)帶數(shù)據(jù)校驗的文件導(dǎo)出
本文主要介紹了go中Excelize處理excel表實現(xiàn)帶數(shù)據(jù)校驗的文件導(dǎo)出,文中通過示例代碼介紹的非常詳細,對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2023-06-06

