Golang new 和 make关键字的区别

Golang中new和make关键字的区别

new

初始化

new(T)是为T类型分配一块内存,将内存清零,并返回T的指针。指针指向的内存是未初始化的(T类型的零值)。

1
2
3
4
func main(){
  a := new(int)
  fmt.Printf("value of a: %#v\n", a) // 输出:value of a: (*int)(0xc000014660)
}

参数

new(T) 中的类型T可以是Golang中的任意类型,如 int, slice, string, map, channel, struct等等

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
func main() {
 // int
 a := new(int)
 fmt.Printf("type of a: %#v\n", reflect.TypeOf(*a).Kind().String())
 // 输出: type of a: "int"

 // string
 b := new(string)
 fmt.Printf("type of b: %#v\n", reflect.TypeOf(*b).Kind().String())
 // 输出: type of b: "string"

 // map
 c := new(map[string]int)
 fmt.Printf("type of c: %#v\n", reflect.TypeOf(*c).Kind().String())
 // 输出: type of c: "map"

 // struct
 d := new(struct{ name string })
 fmt.Printf("type of d: %#v\n", reflect.TypeOf(*d).Kind().String())
 // 输出: type of d: "struct"

 // 指针
  e := new(*int)
 fmt.Printf("type of e: %#v\n", reflect.TypeOf(*e).Kind().String())
  // 输出: type of e: "ptr"

  // slice
 e := new([]int)
 fmt.Printf("value of pointer e: %#v\n", *e)

 // 修改slice
 // 【注意】,slice 与 map 类似,零值是nil,需要修改时,只能通过*e=[]int{},而不能通过下标修改
 //(*e)[0] = 99 // 输出:panic: runtime error: index out of range [0] with length 0
 *e = []int{1, 2, 3}
 fmt.Printf("value of pointer e: %#v\n", *e)
 // 输出:value of pointer e: []int{1, 2, 3}
}

返回值

new(T) 返回的是指针,指向的内存是未初始化的(T类型的零值)。即new(T)*T。如果要访问T的值,需要解引用。即*T

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
func main(){
 // int
 a := new(int)

 // 访问 指针a 的值
 fmt.Printf("value of pointer a: %#v\n", *a)
 // 输出:value of pointer a: 0

 // 修改 int类型的值
 *a = 99
 fmt.Printf("value of pointer a: %#v\n", *a)
 // 输出:value of pointer a: 99

 // --------------------

 // string
 b := new(string)
 fmt.Printf("value of pointer b: %#v\n", *b)
 // 输出: value of pointer a: 0

 // 修改 string类型的值
 *b = "hello"
 fmt.Printf("value of pointer b: %#v\n", *b)
 // 输出:value of pointer b: "hello"

 // --------------------
 
 // map
 c := new(map[string]int)
 fmt.Printf("value of pointer c: %#v\n", *c)

 // 修改 map 类型的值,
 // 【注意】:new(map[T1][T2]) 返回的值`v`是 map 的零值 nil,而不是空map,如果需要修改,只能通过 `v` = map[T1][T2]{},而不能用v[x] = y
 // (*c)["age"] = 20 // 输出:panic: assignment to entry in nil map
 *c = map[string]int{"age": 20}
 fmt.Printf("value of pointer c: %#v\n", *c)
 // 输出:map[string]int{"age":20}

 // --------------------

  // struct
 d := new(struct{ name string })
 fmt.Printf("value of pointer d: %#v\n", *d)
 // 输出:value of pointer d: struct { name string }{name:""}

 // 修改struct
 (*d).name = "zhangsan"
 fmt.Printf("value of pointer d: %#v\n", *d)
 // 输出:value of pointer d: struct { name string }{name:"zhangsan"}
}

make

初始化

make(T[, args])T类型分配一块内存,并初始化内存以及内置的数据结构,返回T的引用而不是指针,并且T的类型只能是slicemapchannel

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
func main() {
 a := make([]int, 0)
 fmt.Printf("value of a: %v\n", a)
 // 输出:value of a: []

 b := make(map[string]int)
 fmt.Printf("value of b: %v\n", b)
 // 输出:value of b: map[]

 c := make(chan int)
 fmt.Printf("value of c: %v\n", c)
 // 输出:value of c: 0xc000090360
}

参数

make(T[, args]) 中的类型T只能是 slice, map 或者 channel中的其中一个。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
func main() {
 // T = slice 需要指定长度,长度大于0时,切片内的元素为make([]T, n)中T的零值
 a := make([]int, 0)
 fmt.Printf("value of a: %v\n", a)
 // 输出:value of a: []

// T = slice
 aMap := make([]map[int]int, 1)
 fmt.Printf("value of aMap: %#v\n", aMap)
 // 输出:value of aMap: []map[int]int{map[int]int(nil)}

// T = map
 b := make(map[string]int)
 fmt.Printf("value of b: %#v\n", b)
 // 输出:value of b: map[]

// T = chan
 c := make(chan int)
 fmt.Printf("value of c: %#v\n", c)
 // 输出:value of c: 0xc000090360
}

返回值

make(T [, args]) T 的返回值为 T 的引用,在函数内部可以直接修改原始值

  • Slice,返回不再是零值,而是一个空切片,可以通过下标修改元素值
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
func main() {
 // Slice,初始化内存和数据结构,返回长度=2的[]int类型切片的引用,
 //【注意】:这里的s可以通过下标直接修改元素值,如s[1]=999,但下标不可以超过`make(t [,args])`中的`args`参数,会导致溢出。
 s := make([]int, 2)
 s[1]=999

 fmt.Printf("value of s is %#v\n", s) // 输出:value of s is []int{0, 999}

 // 修改s的值,在下一行的打印中会生效,说明 `s` 是引用类型
 updateSlice(s)

 // 上面updateSlice内修改的值,在这里依然生效
 fmt.Printf("value of s is updated %#v\n", s) // 输出:value of s is updated []int{99, 999}
}

func updateSlice(s []int) {
 s[0] = 99
}
  • Map,返回的也不是map的零值nil,而是一个空map
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
func main() {
 // Map
 m := make(map[string]string)
 
 fmt.Printf("value of m-1: %#v\n", m) // 输出:value of m-1: map[string]string{}

 // 修改 map,这里m不再是map的零值,而是一个空map,可以直接修改
 m["name"] = "zhangsan"
 fmt.Printf("value of m-2: %#v\n", m) // 输出:value of m-2: map[string]string{"name":"zhangsan"}

 updateMap(m)
 fmt.Printf("value of m-3: %#v\n", m) // 输出:value of m-3: map[string]string{"age":"18", "name":"lisi"}

}

func updateMap(m map[string]string) {
 m["name"] = "lisi"
 m["age"] = "18"
}
  • Channel, 返回的并不是chan的零值nil,而是一个空channel,可以像channel中写入数据,但如果是通过new初始化的channel,则初始化之后的channel是零值nil,写入数据会阻塞,并不会报错
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
func main() {
 ch := make(chan int, 0)
 fmt.Printf("value of ch: %#v\n", ch)

 ch2 := new(chan int)
 fmt.Printf("value of ch2: %#v\n", *ch2)
 ch3 := *ch2
 go func() {
  for {
   if v, ok := <-ch3; ok {
    fmt.Printf("value of ch3: %v\n", v)
    break
   }

   fmt.Println("loop...")
  }
 }()

 fmt.Println("starting insert number to channel...")
 // 【重点】ch3=ch时,这里正常写入数据,因为make返回的是一个空channel;但是如果ch3=*ch2时,这里会阻塞,因为new 返回的*ch2是零值nil,向值为nil的channel写入数据时是会永久阻塞的
 ch3 <- 2
}

总结

new(T) 是为T分配内存空间并将空间初始化为T的零值,返回的是T的指针并且T可以使任意数据类型;而make(T [,args]) 分配内存空间之后会初始化底层的数据结构,返回的是T类型数据结构初始化之后的引用,而不是T的零值,并且T只能是slicemapchannel中的其中一个。

Licensed under CC BY-NC-SA 4.0
皖ICP备20014602号
Built with Hugo
Theme Stack designed by Jimmy