make和new的区别
在Go语言中,make
和new
都是用于内存分配的,但它们之间的主要区别在于它们用于分配不同类型的内存。
new
new是一个内建函数,用于为指定类型分配内存并返回指向它的指针。new
分配的内存会被初始化为零值。new
只接受一个参数,即类型,而不是值。它返回一个指向新分配的类型零值的指针。
示例:
package main import "fmt" type Person struct { Name string Age int } func main() { // 使用 new 为 Person 类型分配内存 p := new(Person) fmt.Println(p) // 输出: &{ 0} // 可以通过指针访问并修改字段的值 p.Name = "Alice" p.Age = 30 fmt.Println(p) // 输出: &{Alice 30} }
在上面的例子中,new(Person)
为Person
类型分配了内存,并返回了一个指向该内存的指针。然后,我们通过该指针修改了Person
字段的值。
疑问:
既然是返回的是指针,修改字段值的时候,为什么不是(*p).Name呢?文心一言给的答案是:
在Go语言中,当使用new
函数为结构体分配内存时,返回的是一个指向该结构体的指针。由于Go语言支持指针的自动解引用(也被称为指针的隐式解引用或指针方法的接收者),因此在访问结构体的字段时,可以直接通过指针来访问,而不需要显式地解引用指针。
示例:
type Person struct { Name string Age int } 使用new函数分配内存后: p := new(Person) 此时p是一个指向Person结构体的指针。由于Go的语法糖,当你通过p.Name来访问Name字段时,Go会自动将p解引用为它所指向的Person实例,然后访问其Name字段。 这种自动解引用的行为是Go语言设计的一部分,它使得指针的使用更加简洁和直观。当然,如果你需要显式地解引用指针,可以使用(*p).Name的形式,但这并不是必须的,因为p.Name已经足够了。 下面是一个完整的例子,展示了如何使用new为结构体分配内存,并直接通过指针访问和修改结构体的字段: package main import "fmt" type Person struct { Name string Age int } func main() { // 使用 new 为 Person 分配内存 p := new(Person) // 直接通过指针访问和修改字段的值 p.Name = "Alice" p.Age = 30 // 打印修改后的值 fmt.Println(p.Name) // 输出: Alice fmt.Println(p.Age) // 输出: 30 // 如果需要显式解引用,可以这样写,但通常不是必须的 fmt.Println((*p).Name) // 输出: Alice fmt.Println((*p).Age) // 输出: 30 } 在这个例子中,你可以看到p.Name和p.Age的用法与(*p).Name和(*p).Age是完全等效的。通常情况下,我们会选择使用更简洁的p.Name和p.Age形式。
make
make
函数用于初始化并返回引用类型的对象(如切片、映射和通道)。它返回一个已经初始化的(非零)引用类型的值,而不是指针。make
接受类型、长度和容量(对于切片和通道)作为参数。
package main import "fmt" func main() { // 使用 make 创建一个切片 s := make([]int, 5) fmt.Println(s) // 输出: [0 0 0 0 0] // 使用 make 创建一个映射 m := make(map[string]int) fmt.Println(m) // 输出: map[] // 使用 make 创建一个通道 c := make(chan int) fmt.Println(c) // 输出: (chan int)(0xc00000e020) }
在上面的例子中,make([]int, 5)
创建了一个长度为5的切片,所有元素都被初始化为零值(对于int
类型,零值是0)。make(map[string]int)
创建了一个空的映射,make(chan int)
创建了一个无缓冲的通道。
总结:
new
用于为指定类型分配内存并返回指向它的指针。它返回的是一个指向零值的指针。make
用于初始化并返回引用类型的对象(如切片、映射和通道)。它返回的是一个已经初始化的引用类型的值,而不是指针。
在大多数情况下,你会使用make
来创建切片、映射和通道,而使用new
的情况相对较少。对于结构体和其他非引用类型,如果你需要动态分配内存并返回指针,那么可以使用new
。但通常,直接使用类型字面量(如p := Person{}
)来创建变量会更常见。