0. 先说结论
Go语言中new和make是内建的两个函数,主要用来创建分配类型内存。
- new可以用于对任何类型的内存分配,并返回指向该内存的指针,且内存中存储的值为对应类型的零值。new不常用,一般不用它。
- make函数只用于slice、map以及channel的内存分配和初始化(非零值)。make无可替代,我们在使用slice、map和channel的时候用make进行初始化。
理解new和make首先从Golang的零值开始。
1. 零值
1.1 何为零值
零值就是变量只有声明没有初始化时系统默认设置的值。以下变量即只有声明没有初始化值,所以会被默认赋值为其对应的零值。
var i int
var f float64
var b bool
var s string
var ip *int
1.2 零值表
类型 | 零值 |
---|---|
bool | false |
uint/uint8/uint16/uint32/uint64 | 0 |
int/int8/int16/int32/int64 | 0 |
float32/float64 | 0 |
complex64/complex128 | 0+0i |
uintptr | 0 |
byte | 0(对应空字符) |
rune | 0 |
string | "" |
struct | 内部属性全部是其对应0值 |
interface | nil |
slice | nil |
map | nil |
chan | nil |
func | nil |
指针 | nil |
2. 内置函数new
2.1 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.
func new(Type) *Type
如源码的英文注释所示,new函数:
- 输入:只有一个参数Type,即分配的内存里存放的变量是什么类型
- 输出:返回指向内存的指针,并且内存中存放的是该类型的零值
2.2 new函数Demo
func main() {
ip := new(int)
fmt.Println("指向int的指针:", ip) //0xc00001a098
fmt.Println("int的零值:", *ip) //0
sp := new(string)
fmt.Println("指向string的指针:", sp) //0xc00004e260
fmt.Println("string的零值:", *sp) //""
pp := new(Person)
fmt.Printf("指向person的指针: %pn", pp) //0xc0000503e0
fmt.Println("person.name的零值:", (*pp).name) //""
fmt.Println("person.phone的零值:", (*pp).phone) //0
fmt.Println("person.id的零值:", (*pp).address) //nil
ipp := new(*int)
fmt.Println("指向int指针的指针:", ipp) //0xc00000a030
fmt.Println("int指针的零值:", *ipp) //nil
mp := new(map[string]string)
fmt.Printf("指向map的指针: %pn", mp) //0xc0000ca028
fmt.Printf("指向map的指针是否为nil: %t", (*mp) == nil) //true
}
type Person struct {
name string
phone int
address *string
}
看下面的这张图便一目了然。
其实,无论是map、slice还是channel其底层都是一个指针,map是一个指向hmap的指针,channel是一个指向hchan的指针,因为创建map、slice、channel时,底层分别返回的是*hmap、*reflect.SliceHeader、*hchan。其中*hchan在我的另一篇文章没名儿:Golang分享(一):channel底层原理中可以得到证明。所以我不建议大家去理解引用,也不用再纠结Go引用和指针的区别了,就是一个语法糖而已。
那么对上述代码我们在main函数最后新增三行初始化map。
(*m) = map[string]string{"张三丰": "武当山", "乔峰": "少林寺"}
fmt.Printf("%pn", (*m)) //0xc00007e4b0
fmt.Printf("%pn", &(*m)) //0xc0000ca028
同样,看下面的这张图便一目了然。
3. 内置函数make
3.1 make的源码
// The make built-in function allocates and initializes an object of type
// slice, map, or chan (only). Like new, the first argument is a type, not a
// value. Unlike new, make's return type is the same as the type of its
// argument, not a pointer to it. The specification of the result depends on
// the type:
//
// Slice: The size specifies the length. The capacity of the slice is
// equal to its length. A second integer argument may be provided to
// specify a different capacity; it must be no smaller than the
// length. For example, make([]int, 0, 10) allocates an underlying array
// of size 10 and returns a slice of length 0 and capacity 10 that is
// backed by this underlying array.
// Map: An empty map is allocated with enough space to hold the
// specified number of elements. The size may be omitted, in which case
// a small starting size is allocated.
// Channel: The channel's buffer is initialized with the specified
// buffer capacity. If zero, or the size is omitted, the channel is
// unbuffered.
func make(t Type, size ...IntegerType) Type
如源码的英文注释所示,make函数:
- 输入:一个仅限channel、map、slice的参数Type。第二个参数用于指定长度length,第三个参数用于指定容量capacity
- 输出:一个指定长度和容量的channel、map或者slice
make也是用于内存分配的,但是和new不同,它只用于channel、map以及slice的内存创建,而且它返回的类型就是这三个类型本身,而不是他们的指针类型。
3.2 make函数的Demo
func main() {
mapDemo := make(map[string]string)
fmt.Println("mapDemo: ", mapDemo) //demo: map[]
chanDemo := make(chan int, 10)
fmt.Println("chanDemo: ", chanDemo) //chanDemo: 0xc000116000
sliceDemo := make([]int, 10)
fmt.Println("sliceDemo: ", sliceDemo) //sliceDemo: [0 0 0 0 0 0 0 0 0 0]
}
4.关于零值和初始化的理解
func main() {
//先看make的初始化
mp1 := make(map[string]string)
mp1["乔峰"] = "少林寺"
fmt.Println(mp1)
//再看new的置零值
mp := new(map[string]string)
fmt.Printf("指向map的指针是否为nil: %tn", (*mp) == nil) //true
//以下这行会报错,因为*mp被置为零值nil,没有初始化,
(*mp)["张三丰"] = "武当山"
}
new函数:返回指向map的指针,而map是零值nil,故而无法进行set操作
make函数:返回map(map本质就是指向hmap的指针),而map已经被初始化(非零值),故而可以直接进行set操作
文章来源于互联网:Golang分享(二):new和make的区别