go 中 make和new關(guān)鍵字的區(qū)別及實現(xiàn)原理

Go語言中 new 和 make 是兩個內(nèi)置函數(shù),主要用來創(chuàng)建并分配類型的內(nèi)存蹂随。

在我們定義變量的時候十嘿,可能會覺得有點迷惑,不知道應(yīng)該使用哪個函數(shù)來聲明變量岳锁,其實他們的規(guī)則很簡單:

  • new 只分配內(nèi)存;

  • 而 make 只能用于 slice绩衷、map 和 channel 的初始化;

下面我們就來具體介紹一下。

new

在Go語言中激率,new 函數(shù)描述如下:

// The new built-in function allocates memory. The first argument is a type,
// not a value, and the value returned is a pointer to a newly
// allocated zero value of that type.func new(Type) *Type

從上面的代碼可以看出咳燕,new 函數(shù)只接受一個參數(shù),這個參數(shù)是一個類型乒躺,并且返回一個指向該類型內(nèi)存地址的指針招盲。

同時 new 函數(shù)會把分配的內(nèi)存置為零,也就是類型的零值嘉冒。

【示例】使用 new 函數(shù)為變量分配內(nèi)存空間曹货。

var sum *int
sum = new(int) //分配空間
*sum = 98
fmt.Println(*sum)

當(dāng)然,new 函數(shù)不僅僅能夠為系統(tǒng)默認(rèn)的數(shù)據(jù)類型讳推,分配空間顶籽,自定義類型也可以使用 new 函數(shù)來分配空間,如下所示:

type Student struct {
   name string
   age int
}

var s *Student
s = new(Student) //分配空間
s.name ="dequan"

fmt.Println(s)

這里如果我們不使用 new 函數(shù)為自定義類型分配空間(將第 7 行注釋)银觅,就會報錯:

panic: runtime error: invalid memory address or nil pointer dereference
[signal SIGSEGV: segmentation violation code=0x1 addr=0x0 pc=0x80bd277]
goroutine 1 [running]:

這就是 new 函數(shù)礼饱,它返回的永遠(yuǎn)是類型的指針,指針指向分配類型的內(nèi)存地址设拟。

make

make 也是用于內(nèi)存分配的慨仿,但是和 new 不同久脯,它只用于 chan纳胧、map 以及 slice 的內(nèi)存創(chuàng)建,而且它返回的類型就是這三個類型本身帘撰,而不是他們的指針類型跑慕,因為這三種類型就是引用類型,所以就沒有必要返回他們的指針了摧找。

在Go語言中核行,make 函數(shù)的描述如下:

// The make built-in function allocates(分配) and initializes(初始化) an object of type
// slice, map, or chan (only). Like new(正如 new 關(guān)鍵字), the first argument is a type(第一個參數(shù)是一個類型), not a value (而不是一個值)。Unlike new(又不像 new 關(guān)鍵字), make's return type is the same as the type of its
// argument, not a pointer to it. (返回參數(shù)類型蹬耘,而不是該類型的指針)
// The specification of the result depends on the type:(返回的結(jié)果依賴于類型)
// Slice(切片): The size specifies the length(size 參數(shù)指定切片的長度). The capacity of the slice is equal to its length(切片的容量等于切片的長度). A second integer argument may be provided to specify a different capacity(可以使用第二個整型參數(shù)來指定切片的容量); it must be no smaller than the length(容量必須大于長度), so make([]int, 0, 10) allocates a slice of length 0 and capacity 10.
// Map(字典): An empty map is allocated with enough space to hold the
// specified number of elements. The size may be omitted, in which case
// a small starting size is allocated.
// Channel: The channel's buffer is initialized with the specified
// buffer capacity. If zero, or the size is omitted, the channel is
// unbuffered.
func make(t Type, size ...IntegerType) Type
  • t:類型

  • size:可變參數(shù)芝雪,整型

  • 切片:make([]int, 0, 10)

    • 一個size 參數(shù)時,則視為指定切片的長度和容量相等综苔,均為 size
    • 兩個 size 參數(shù)時惩系,第一個為切片的長度位岔,第二個為切片的容量。
  • 字典:一個空字典被分配足夠的空間去容納指定參數(shù)的元素堡牡。

    • size參數(shù)可以省略,這種情況下分配一個小的空間給字典晤柄。
  • 通道:分配通道緩沖區(qū)的大小為指定的大小擦剑,如果沒有指定,則通道沒有緩沖區(qū)芥颈。

通過上面的代碼可以看出 make 函數(shù)的 t 參數(shù)必須是 chan(通道)惠勒、map(字典)、slice(切片)中的一個爬坑,并且返回值也是類型本身捉撮。

注意:make 函數(shù)只用于 map,slice 和 channel妇垢,并且不返回指針巾遭。如果想要獲得一個顯式的指針,可以使用 new 函數(shù)進(jìn)行分配闯估,或者顯式地使用一個變量的地址灼舍。

Go語言中的 new 和 make 主要區(qū)別如下:

  • make 只能用來分配及初始化類型為 slice、map涨薪、chan 的數(shù)據(jù)骑素。new 可以分配任意類型的數(shù)據(jù);
  • new 分配返回的是指針刚夺,即類型 *Type献丑。make 返回引用,即 Type侠姑;
  • new 分配的空間被清零创橄。make 分配空間后,會進(jìn)行初始化莽红。

實現(xiàn)原理

接下來我們將分別介紹一下 make 和 new 在初始化不同數(shù)據(jù)結(jié)構(gòu)時的具體過程妥畏,我們會從編譯期間和運行時兩個不同的階段理解這兩個關(guān)鍵字的原理。

make

我們已經(jīng)了解了 make 在創(chuàng)建 slice安吁、map 和 channel 的具體過程醉蚁,所以在這里我們也只是會簡單提及 make 相關(guān)的數(shù)據(jù)結(jié)構(gòu)初始化原理。

image.png

在編譯期的類型檢查階段鬼店,Go語言其實就將代表 make 關(guān)鍵字的 OMAKE 節(jié)點根據(jù)參數(shù)類型的不同轉(zhuǎn)換成了 OMAKESLICE网棍、OMAKEMAP 和 OMAKECHAN 三種不同類型的節(jié)點,這些節(jié)點最終也會調(diào)用不同的運行時函數(shù)來初始化數(shù)據(jù)結(jié)構(gòu)妇智。

new

內(nèi)置函數(shù) new 會在編譯期的 SSA 代碼生成階段經(jīng)過 callnew 函數(shù)的處理滥玷,如果請求創(chuàng)建的類型大小是 0捌锭,那么就會返回一個表示空指針的 zerobase 變量,在遇到其他情況時會將關(guān)鍵字轉(zhuǎn)換成 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
}

需要提到的是罗捎,哪怕當(dāng)前變量是使用 var 進(jìn)行初始化观谦,在這一階段也可能會被轉(zhuǎn)換成 newobject 的函數(shù)調(diào)用并在堆上申請內(nèi)存:

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())
        }
    }
}

當(dāng)然這也不是絕對的,如果當(dāng)前聲明的變量或者參數(shù)不需要在當(dāng)前作用域外生存桨菜,那么其實就不會被初始化在堆上豁状,而是會初始化在當(dāng)前函數(shù)的棧中并隨著函數(shù)調(diào)用的結(jié)束而被銷毀。

newobject 函數(shù)的工作就是獲取傳入類型的大小并調(diào)用 mallocgc 在堆上申請一片大小合適的內(nèi)存空間并返回指向這片內(nèi)存空間的指針:

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

總結(jié)

最后倒得,簡單總結(jié)一下Go語言中 make 和 new 關(guān)鍵字的實現(xiàn)原理泻红,make 關(guān)鍵字的主要作用是創(chuàng)建 slice、map 和 Channel 等內(nèi)置的數(shù)據(jù)結(jié)構(gòu)霞掺,而 new 的主要作用是為類型申請一片內(nèi)存空間谊路,并返回指向這片內(nèi)存的指針。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末菩彬,一起剝皮案震驚了整個濱河市缠劝,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌骗灶,老刑警劉巖惨恭,帶你破解...
    沈念sama閱讀 207,113評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異耙旦,居然都是意外死亡脱羡,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,644評論 2 381
  • 文/潘曉璐 我一進(jìn)店門免都,熙熙樓的掌柜王于貴愁眉苦臉地迎上來锉罐,“玉大人,你說我怎么就攤上這事绕娘∨Ч妫” “怎么了?”我有些...
    開封第一講書人閱讀 153,340評論 0 344
  • 文/不壞的土叔 我叫張陵业舍,是天一觀的道長抖拦。 經(jīng)常有香客問我,道長舷暮,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 55,449評論 1 279
  • 正文 為了忘掉前任噩茄,我火速辦了婚禮下面,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘绩聘。我一直安慰自己沥割,他們只是感情好耗啦,可當(dāng)我...
    茶點故事閱讀 64,445評論 5 374
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著机杜,像睡著了一般帜讲。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上椒拗,一...
    開封第一講書人閱讀 49,166評論 1 284
  • 那天似将,我揣著相機與錄音,去河邊找鬼蚀苛。 笑死在验,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的堵未。 我是一名探鬼主播腋舌,決...
    沈念sama閱讀 38,442評論 3 401
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼渗蟹!你這毒婦竟也來了块饺?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 37,105評論 0 261
  • 序言:老撾萬榮一對情侶失蹤雌芽,失蹤者是張志新(化名)和其女友劉穎刨沦,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體膘怕,經(jīng)...
    沈念sama閱讀 43,601評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡想诅,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,066評論 2 325
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了岛心。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片来破。...
    茶點故事閱讀 38,161評論 1 334
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖忘古,靈堂內(nèi)的尸體忽然破棺而出徘禁,到底是詐尸還是另有隱情,我是刑警寧澤髓堪,帶...
    沈念sama閱讀 33,792評論 4 323
  • 正文 年R本政府宣布送朱,位于F島的核電站,受9級特大地震影響干旁,放射性物質(zhì)發(fā)生泄漏驶沼。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 39,351評論 3 307
  • 文/蒙蒙 一争群、第九天 我趴在偏房一處隱蔽的房頂上張望回怜。 院中可真熱鬧,春花似錦换薄、人聲如沸玉雾。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,352評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽复旬。三九已至垦缅,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間驹碍,已是汗流浹背壁涎。 一陣腳步聲響...
    開封第一講書人閱讀 31,584評論 1 261
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留幸冻,地道東北人粹庞。 一個月前我還...
    沈念sama閱讀 45,618評論 2 355
  • 正文 我出身青樓,卻偏偏與公主長得像洽损,于是被迫代替她去往敵國和親庞溜。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 42,916評論 2 344

推薦閱讀更多精彩內(nèi)容