英文:
Difference in Go between allocating memory by new(Type) and &Type{}
问题
考虑以下示例:
type House struct{}
func main() {
house1 := new(House)
house2 := &House{}
fmt.Printf("%T | %T\n", house1, house2)
}
输出结果为:*main.House | *main.House
这两个赋值语句都产生了指向类型为 House
的指针(来自 main 包)。
根据 new
的 Go 文档:
// new 内置函数用于分配内存。第一个参数是类型,而不是值,返回的值是指向新分配的该类型的零值的指针。
在这两个赋值语句中,内存分配在技术上是相同的吗?是否有最佳实践?
英文:
Consider the following example:
type House struct{}
func main() {
house1 := new(House)
house2 := &House{}
fmt.Printf("%T | %T\n", house1, house2)
}
Output: *main.House | *main.House
Both assignments produce a pointer to type House
(from package main).
From the go docu of new
:
// 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.
Is memory allocation in both assignments technically the same? Is there a best practice?
答案1
得分: 5
内存分配在这两种赋值中在技术上是相同的吗?
我不会讨论具体的实现细节,因为这些可能会改变。
从语言规范的角度来看,存在一些差异。
使用new(T)
进行分配遵循分配规则(我的注释以[斜体]表示):
内置函数
new
接受类型T
,在运行时为该类型的变量分配存储空间,并返回指向它的类型为*T
的值。变量的初始化方式如初始值部分所述。[它的零值]
使用复合字面量&T{}
进行分配遵循复合字面量和取址操作的规则:
复合字面量用于构造结构体、数组、切片和映射的值,并在每次评估时创建一个新值。
对复合字面量取地址会生成一个指向使用字面量的值初始化的唯一变量的指针。
因此,new(T)
和&T{}
都会分配存储空间并产生类型为*T
的值。然而:
new(T)
接受任何类型,包括预声明的标识符如int
和bool
,如果你只需要用一行代码初始化这些变量,这可能会很方便:
n := new(int) // n的类型是*int,指向0
b := new(bool) // b的类型是*bool,指向false
new
(引用自《Effective Go》)"不会初始化<sup>1</sup>该内存,只会将其置零"。也就是说,内存位置将具有类型的零值。- 复合字面量只能用于结构体、数组、切片和映射
- 复合字面量可以通过显式初始化结构体字段或数组/切片/映射项来构造非零值
总之,正如《Effective Go》所指出的(与上面的段落相同):
如果复合字面量根本不包含字段,它将为该类型创建一个零值。表达式
new(T)
和&T{}
是等价的。
<hr>
[1]:规范和《Effective Go》在使用术语"初始化"时存在不一致。要记住的是,使用new
只能得到一个零值,而复合字面量允许构造非零值。显然,规范是真相的来源。
英文:
> Is memory allocation in both assignments technically the same?
I'm not going to talk about implementation details, since those may or may not change.
From the point of view of the language specifications, there is a difference.
Allocating with new(T)
follows the rules of allocation (my own note in [italics]):
> The built-in function new
takes a type T
, allocates storage for a variable of that type at run time, and returns a value of type *T
pointing to it. The variable is initialized as described in the section on initial values. [its zero value]
Allocating with a composite literal as &T{}
follows the rules of composite literals and addressing:
> Composite literals construct values for structs, arrays, slices, and maps and create a new value each time they are evaluated.
> Taking the address of a composite literal generates a pointer to a unique variable initialized with the literal's value.
So both new(T)
and &T{}
allocate storage and yield values of type *T
. However:
new(T)
accepts any type, including predeclared identifiers asint
andbool
, which may come in handy if you just need to initialize such vars with a one-liner:
n := new(int) // n is type *int and points to 0
b := new(bool) // b is type *bool and points to false
new
(quoting Effective Go) "does not initialize<sup>1</sup> that memory, it only zeros it". I.e. the memory location will have the type's zero value.- composite literals can be used only for structs, arrays, slices and maps
- composite literals can construct non-zero values by explicitly initializing struct fields or array/slice/map items
Bottom line: as Effective Go points out (same paragraph as above):
> if a composite literal contains no fields at all, it creates a zero value for the type. The expressions new(T)
and &T{}
are equivalent.
<hr>
[1]: there's an inconsistency in the use of the term "initialization" in the specs and Effective Go. The take-away is that with new
you can only yield a zero-value, whereas a composite literal allows you to construct non-zero values. And obviously the specs are the source of truth.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论