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

