声明变量时,T
某种类型在哪里:
var name T
Go 给你一块未初始化的“归零”内存。
对于原语,这意味着var name int
它将是 0,并且var name string
将是“”。在C 中,它可能被归零,或者可能是一些意想不到的东西。Go 保证未初始化的变量是类型的零等价物。
在内部,切片、映射和通道被视为指针。指针零值为零,这意味着它指向零内存。如果不初始化它,如果您尝试对其进行操作,您可能会遇到恐慌。
该make
函数专为切片、地图或通道而设计。make 函数的参数是:
make(T type, length int[, capacity int]) // For slices.
make(T[, capacity int]) // For a map.
make(T[, bufferSize int]) // For a channel. How many items can you take without blocking?
切片length
是它开始的项目数。容量是需要调整大小之前分配的内存(内部,新大小 * 2,然后复制)。有关更多信息,请参阅有效 Go:使用 make 进行分配。
结构:new(T)
等价于&T{}
,而不是T{}
。*new(T)
相当于*&T{}
。
切片:make([]T,0)
相当于[]T{}
.
地图:make(map[T]T)
相当于map[T]T{}
.
至于首选哪种方法,我问自己以下问题:
我现在知道函数内部的值吗?
如果答案是“是”,那么我会选择上述之一T{...}
。如果答案是“否”,那么我使用 make 或 new。
例如,我会避免这样的事情:
type Name struct {
FirstName string
LastName string
}
func main() {
name := &Name{FirstName:"John"}
// other code...
name.LastName = "Doe"
}
相反,我会做这样的事情:
func main() {
name := new(Name)
name.FirstName = "John"
// other code...
name.LastName = "Doe"
}
为什么?因为通过使用new(Name)
我明确表示我打算稍后填充这些值。如果我使用&Name{...}
它就不清楚我打算稍后在同一个函数中添加/更改一个值而不阅读其余代码。
当您不需要指针时,结构例外。我将使用T{}
,但如果我打算添加/更改值,我不会在其中添加任何内容。当然*new(T)
也可以,但这就像使用*&T{}
. T{}
在这种情况下更干净,尽管我倾向于使用带有结构的指针来避免在传递它时制作副本。
要记住的另一件事是, a比 struct[]*struct
更小,调整大小更便宜[]struct
,假设结构比指针大得多,指针通常为 4 - 8 个字节(64 位上为 8 个字节?)。