3 回答

TA貢獻(xiàn)1809條經(jīng)驗(yàn) 獲得超8個(gè)贊
[...] 有什么“新”可以做而 &T{} 不能做的事情,反之亦然?
我能想到三個(gè)不同點(diǎn):
“復(fù)合文字”語(yǔ)法(的
T{}
一部分&T{}
)僅適用于“結(jié)構(gòu)、數(shù)組、切片和映射”[鏈接],而該new
函數(shù)適用于任何類型[鏈接]。對(duì)于結(jié)構(gòu)或數(shù)組類型,
new
函數(shù)始終為其元素生成零值,而復(fù)合文字語(yǔ)法允許您根據(jù)需要將某些元素初始化為非零值。對(duì)于切片或映射類型,
new
函數(shù)始終返回指向 的指針nil
,而復(fù)合文字語(yǔ)法始終返回已初始化的切片或映射。(對(duì)于地圖來(lái)說(shuō),這非常重要,因?yàn)槟荒芟?. 中添加元素nil
。)此外,復(fù)合字面量語(yǔ)法甚至可以創(chuàng)建非空切片或地圖。
(第二個(gè)和第三個(gè)要點(diǎn)實(shí)際上是同一件事的兩個(gè)方面——new
函數(shù)總是創(chuàng)建零值——但我將它們分開(kāi)列出,因?yàn)椴煌愋偷暮x有點(diǎn)不同。)

TA貢獻(xiàn)1829條經(jīng)驗(yàn) 獲得超6個(gè)贊
見(jiàn)ruak 的回答。不過(guò),我想指出一些內(nèi)部實(shí)現(xiàn)細(xì)節(jié)。你不應(yīng)該在生產(chǎn)代碼中使用它們,但它們有助于闡明幕后真正發(fā)生的事情,在 Go 運(yùn)行時(shí)。
本質(zhì)上,切片由三個(gè)值表示。包reflect導(dǎo)出一個(gè)類型,SliceHeader:
SliceHeader 是切片的運(yùn)行時(shí)表示。它不能安全或便攜地使用,它的表示可能會(huì)在以后的版本中發(fā)生變化。此外,Data 字段不足以保證它引用的數(shù)據(jù)不會(huì)被垃圾收集,因此程序必須保留一個(gè)單獨(dú)的、正確類型的指向底層數(shù)據(jù)的指針。
type SliceHeader struct {
Data uintptr
Len int
Cap int
}
如果我們使用它來(lái)檢查類型變量[]T(對(duì)于任何類型T),我們可以看到三個(gè)部分:指向底層數(shù)組的指針、長(zhǎng)度和容量。在內(nèi)部,切片值v始終具有所有這三個(gè)部分。我認(rèn)為應(yīng)該保持一個(gè)一般條件,如果你不使用unsafe它來(lái)打破它,通過(guò)檢查它似乎會(huì)保持(無(wú)論如何基于有限的測(cè)試):
該Data字段不為零(在這種情況下Len,并且Cap可以但不必非零),或者
該Data字段為零(在這種情況下Len和Cap都應(yīng)該為零)。
如果該字段為零,則該切片值v是。nilData
通過(guò)使用unsafe包裝,我們可以故意破壞它(然后將其全部放回原處——希望在我們破壞它的同時(shí)不會(huì)出現(xiàn)任何問(wèn)題),從而檢查碎片。當(dāng)Go Playground 上的這段代碼運(yùn)行時(shí)(下面也有一個(gè)副本),它會(huì)打?。?/p>
via &literal: base of array is 0x1e52bc; len is 0; cap is 0.
Go calls this non-nil.
via new: base of array is 0x0; len is 0; cap is 0.
Go calls this nil even though we clobbered len() and cap()
Making it non-nil by unsafe hackery, we get [42] (with cap=1).
after setting *p1=nil: base of array is 0x0; len is 0; cap is 0.
Go calls this nil even though we clobbered len() and cap()
Making it non-nil by unsafe hackery, we get [42] (with cap=1).
代碼本身有點(diǎn)長(zhǎng),所以我把它留到最后(或使用上面的 Playground 鏈接)。但它表明p == nil源代碼中的實(shí)際測(cè)試僅編譯為對(duì)該Data字段的檢查。
當(dāng)你這樣做時(shí):
p2 := new([]int)
該new函數(shù)實(shí)際上只分配切片頭。它將所有三個(gè)部分設(shè)置為零并返回指向結(jié)果標(biāo)頭的指針。因此*p2,其中包含三個(gè)零字段,這使其成為正確的nil值。
另一方面,當(dāng)你這樣做時(shí):
p1 := &[]int{}
Go 編譯器構(gòu)建一個(gè)空數(shù)組(大小為零,保持零整數(shù)),然后構(gòu)建一個(gè)切片頭:指針部分指向空數(shù)組,長(zhǎng)度和容量設(shè)置為零。然后p1使用非零Data字段指向此標(biāo)頭。稍后的賦值*p1 = nil將零寫(xiě)入所有三個(gè)字段。
讓我用粗體字重復(fù)一遍:這些不是語(yǔ)言規(guī)范所承諾的,它們只是實(shí)際的實(shí)現(xiàn)。
地圖的工作方式非常相似。map 變量實(shí)際上是一個(gè)指向map header的指針。map headers 的細(xì)節(jié)比 slice headers 更難訪問(wèn):它們沒(méi)有reflect類型。實(shí)際實(shí)現(xiàn)可在此處type hmap查看(請(qǐng)注意,它未導(dǎo)出)。
這意味著m2 := new(map[T1]T2)實(shí)際上只分配一個(gè)指針,并將該指針本身設(shè)置為 nil。沒(méi)有實(shí)際的地圖!該new函數(shù)返回 nil 指針,然后m2是nil. 同樣,只需在tovar m1 map[T1]T2中設(shè)置一個(gè)簡(jiǎn)單的指針值。但是分配一個(gè)實(shí)際的結(jié)構(gòu),填充它,并指出它。我們可以再次窺視 Go Playground 的幕后,使用不保證明天可以工作的代碼,看看它的效果。m1nilvar m3 map[T1]T2{}hmapm3
作為編寫(xiě) Go 程序的人,你不需要知道這些。但是,如果您使用過(guò)低級(jí)語(yǔ)言(例如匯編和 C),這些可以解釋很多。特別是,這些解釋了為什么不能插入到 nil 映射中:映射變量本身保存一個(gè)指針值,并且在映射變量本身有一個(gè)指向(可能為空的)映射頭的非零指針之前,沒(méi)有辦法做插入。插入可以分配一個(gè)新映射并插入數(shù)據(jù),但映射變量不會(huì)指向正確的hmap標(biāo)頭對(duì)象。
(語(yǔ)言作者可以通過(guò)使用第二級(jí)間接來(lái)完成這項(xiàng)工作:映射變量可以是指向指向映射頭的變量的指針?;蛘咚麄兛梢允褂成渥兞渴冀K指向頭,并new實(shí)際上分配了一個(gè)標(biāo)頭,方式make是這樣;那么永遠(yuǎn)不會(huì)有一個(gè) nil 映射。但是他們沒(méi)有做這些,我們得到了我們得到的,這很好:你只需要知道初始化映射。)
這是切片檢查器。(使用操場(chǎng)鏈接查看地圖檢查器:鑒于我必須將hmap' 的定義從運(yùn)行時(shí)復(fù)制出來(lái),我希望它特別脆弱且不值得展示。切片頭的結(jié)構(gòu)似乎不太可能隨著時(shí)間而改變。 )
package main
import (
"fmt"
"reflect"
"unsafe"
)
func main() {
p1 := &[]int{}
p2 := new([]int)
show("via &literal", *p1)
show("\nvia new", *p2)
*p1 = nil
show("\nafter setting *p1=nil", *p1)
}
// This demonstrates that given a slice (p), the test
// if p == nil
// is really a test on p.Data. If it's zero (nil),
// the slice as a whole is nil. If it's nonzero, the
// slice as a whole is non-nil.
func show(what string, p []int) {
pp := unsafe.Pointer(&p)
sh := (*reflect.SliceHeader)(pp)
fmt.Printf("%s: base of array is %#x; len is %d; cap is %d.\n",
what, sh.Data, sh.Len, sh.Cap)
olen, ocap := len(p), cap(p)
sh.Len, sh.Cap = 1, 1 // evil
if p == nil {
fmt.Println(" Go calls this nil even though we clobbered len() and cap()")
answer := 42
sh.Data = uintptr(unsafe.Pointer(&answer))
fmt.Printf(" Making it non-nil by unsafe hackery, we get %v (with cap=%d).\n",
p, cap(p))
sh.Data = 0 // restore nil-ness
} else {
fmt.Println("Go calls this non-nil.")
}
sh.Len, sh.Cap = olen, ocap // undo evil
}

TA貢獻(xiàn)1797條經(jīng)驗(yàn) 獲得超6個(gè)贊
對(duì)于結(jié)構(gòu)和其他復(fù)合材料,兩者都是相同的。
t1:=&T{}
t2:=new(T)
//Both are same
您不能在不使用new. 您需要?jiǎng)?chuàng)建一個(gè)命名變量,然后獲取其地址。
func newInt() *int {
return new(int)
}
func newInt() *int {
// return &int{} --> invalid
var dummy int
return &dummy
}
- 3 回答
- 0 關(guān)注
- 173 瀏覽
添加回答
舉報(bào)