
在go语言中,进行内存分配和值初始化有多种方式,包括:
理解这些机制有助于我们选择最合适的内存管理方式。
new 函数是Go语言中一个通用的内存分配器。它的主要功能是:
语法: new(Type)
示例:
立即学习“go语言免费学习笔记(深入)”;
package main
import "fmt"
type Point struct {
X, Y int
}
func main() {
// 为Point类型分配内存,并返回*Point类型的指针
p1 := new(Point)
fmt.Printf("p1 类型: %T, 值: %+v\n", p1, p1) // p1 类型: *main.Point, 值: &{X:0 Y:0}
// 为int类型分配内存,并返回*int类型的指针
i1 := new(int)
fmt.Printf("i1 类型: %T, 值: %v\n", i1, *i1) // i1 类型: *int, 值: 0
// 对比复合字面量:&Point{} 结合了分配和初始化
p2 := &Point{}
fmt.Printf("p2 类型: %T, 值: %+v\n", p2, p2) // p2 类型: *main.Point, 值: &{X:0 Y:0}
p3 := &Point{X: 2, Y: 3}
fmt.Printf("p3 类型: %T, 值: %+v\n", p3, p3) // p3 类型: *main.Point, 值: &{X:2 Y:3}
// 注意:&int 是非法的,因为int是一个值类型,不能直接取其类型地址。
// 但 new(int) 是合法的,它分配了一个int的内存并返回其指针。
// var i int
// i4 := &i // 合法,获取已存在变量i的地址
}从示例中可以看出,new(Point) 和 &Point{} 都能得到 *Point 类型的值,但后者允许在分配的同时进行字段初始化。对于基本类型如 int,new(int) 是分配零值 int 并返回其指针的常用方式,因为直接 &int 是语法错误的。
make 函数与 new 不同,它不是通用的内存分配器。make 专用于分配并初始化三种内建的引用类型:切片(slice)、映射(map) 和 通道(channel)。这三种类型在Go语言中是特殊的存在,它们不仅仅是内存块,还需要内部数据结构(如切片头、哈希表、缓冲区等)进行初始化才能正常使用。
语法:
make 函数会返回一个已初始化且可用的类型实例本身,而不是指针。
示例:
立即学习“go语言免费学习笔记(深入)”;
package main
import "fmt"
func main() {
// 切片:分配一个长度为5,容量为10的int切片
s := make([]int, 5, 10)
fmt.Printf("s 类型: %T, 值: %v, 长度: %d, 容量: %d\n", s, s, len(s), cap(s))
// s 类型: []int, 值: [0 0 0 0 0], 长度: 5, 容量: 10
// 映射:分配一个string到int的映射,并初始化其内部哈希表
m := make(map[string]int)
fmt.Printf("m 类型: %T, 值: %v\n", m, m)
// m 类型: map[string]int, 值: map[]
m["key"] = 10
fmt.Println("m['key']:", m["key"])
// 通道:分配一个int类型的通道,带有一个缓冲区
c := make(chan int, 1)
fmt.Printf("c 类型: %T, 值: %v\n", c, c)
// c 类型: chan int, 值: 0xc000060060 (通道的内部表示)
c <- 1
val := <-c
fmt.Println("从通道接收到的值:", val)
// 注意:make(Point) 或 make(int) 是非法的,因为make不能用于非引用类型
// make(Point) // 编译错误
// make(int) // 编译错误
}可以看到,make 返回的是 []int、map[string]int、chan int 这些类型本身,而不是它们的指针。这是因为这些类型在使用前需要进行特定的初始化步骤,而make正是负责完成这些步骤。
理解 new 和 make 的核心差异是掌握Go内存管理的关键:
返回类型:
p := new(chan int) // p 的类型是 *chan int c := make(chan int) // c 的类型是 chan int
这里 p 是一个指向 nil 通道的指针,需要进一步解引用并赋值一个 make 创建的通道才能使用。而 c 已经是可以直接使用的通道。
适用类型:
初始化行为:
Go语言的设计者选择保留 new 和 make 两个独立的函数,而非合并为一个,主要是出于清晰性和避免混淆的考虑。
试想如果只有一个名为 NEW 的函数:
这种统一的 NEW 函数在处理引用类型时,需要根据参数是否带有 * 来区分是分配指针还是初始化实例,这无疑会增加学习曲线和使用时的心智负担。例如,NEW(chan int) 返回 chan int,而 NEW(*chan int) 返回 *chan int,这种细微的语法差异可能会导致开发者混淆。
通过将功能明确地划分为 new(通用零值内存分配,返回指针)和 make(引用类型初始化,返回实例),Go语言使得这两种操作的目的和结果更加直观,降低了新Go程序员的理解难度。
使用 new:
使用 make:
结构体初始化: 对于结构体,通常推荐使用复合字面量 &MyStruct{Field: value} 的形式,它结合了分配和初始化,代码更简洁易读。
理解 new 和 make 的区别,并根据具体场景选择合适的函数,是编写高效、健壮Go程序的关键一步。
以上就是深入理解Go语言中的new与make:内存分配与类型初始化的详细内容,更多请关注php中文网其它相关文章!
每个人都需要一台速度更快、更稳定的 PC。随着时间的推移,垃圾文件、旧注册表数据和不必要的后台进程会占用资源并降低性能。幸运的是,许多工具可以让 Windows 保持平稳运行。
Copyright 2014-2025 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号