欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

Go里面的new和make

程序员文章站 2024-03-25 16:46:10
...

1、如果变量定义时没有初始化,那么初值是多少?

package main

import (
	"fmt"
	"strings"
)

func main() {
	var a int
	fmt.Println("a-->", a)
	var b string
	fmt.Println("b is empty-->", strings.EqualFold(b, ""))
}

a--> 0
b is empty--> true

可见,对于值类型的变量,定义的时候就会分配内存,并赋默认初值

package main

import (
	"fmt"
)

func main() {
	var c *int
	fmt.Println("c is nil-->", c == nil)
}

对于引用类型的变量,定义的时候会统一指向nil,不会分配相应类型的内存

2、如果在初始化时需要为指针分配内存应该怎么做?那就用new(关于new的简介见《Go语言第五课 指针》)

package main

import (
	"fmt"
)

func main() {
	var c *int
	fmt.Println("c is nil-->", c == nil)

	var d *int = new(int)
	fmt.Println("d-->", *d)
}

c is nil--> true
d--> 0
可见,new的作用就是:为指针分配内存空间,并将内存空间初始化为对应类型的默认初值

3、make只能建立slice,map和channel三种类型;new没有限制(见《Go语言第十二课 集合类型》)

那么make和new在创建slice,map和channel三种类型时有什么区别?

package main

import "fmt"

func main() {
	e := make([]int, 0)
	fmt.Printf("e at %p \n", &e)
	e = append(e, 0)
	fmt.Printf("e at %p \n", &e)
	fmt.Println("e-->", e)

	f := *new([]int)
	fmt.Printf("f at %p \n", &f)
	f = append(f, 0)
	fmt.Printf("f at %p \n", &f)
	fmt.Println("f-->", f)
}

e at 0xc0000ac020 
e at 0xc0000ac020 
e--> [0]
f at 0xc0000ac060 
f at 0xc0000ac060 
f--> [0]
这样看来,new和make都可以创建slice,没有任何区别。那么map、channel两种类型呢?

package main

import "fmt"

func main() {
	g := make(map[int]int)
	g[1] = 111
	fmt.Println("g-->", g)

	f := *new(map[int]int)
	f[1] = 111
	fmt.Println("f-->", f)
}

g--> map[1:111]
panic: assignment to entry in nil map

goroutine 1 [running]:
main.main()
        /home/yong/go/src/awesomeProject2/main.go:11 +0xff
上面的代码语法没有错,但是执行的时候报错。这是因为相比make,new只会分配内存、赋默认初值并返回指针,缺少初始化功能。所以经new创建出来的map、channel类型的变量实际上无法使用。所以slice,map和channel三种类型的变量尽量用make创建。

4、make创建出来的变量是值类型还是指针类型?

package main

import "fmt"

func main() {
	i := make(map[int]int)
	i[1] = 111
	fmt.Println("i-->", i)

	j := i
	j[2] = 222
	fmt.Println("j-->", j)
	fmt.Println("i-->", i)
}

从这段代码来看,改变j会影响到i,显然i,j指向的是同一片内存,这就间接证明了i和j是指针类型(否则不会相互影响)。为了彻底弄清楚这个问题,我们熟悉一个函数:

fmt.Sprintf

这个函数的%p占位符专门用于格式化内存地址值,例如

package main

import "fmt"

func main() {
	var k int
	k_add := fmt.Sprintf("k address-->%p", k)
	fmt.Println(k_add)
	k_add = fmt.Sprintf("k address-->%p", &k)
	fmt.Println(k_add)
}

k address-->%!p(int=0)
k address-->0xc00001e0a8
可以看到,如果格式化的对象不是地址值的话,得到的字符就会显示异常,我们用这个函数来看看make创建的对象地址信息

package main

import (
	"fmt"
	"reflect"
)

type MyType *int

func main() {
	l := make(map[int]int)
	l[1] = 111
	fmt.Println(fmt.Sprintf("l at -->%p", l))
	fmt.Println(fmt.Sprintf("l p at -->%p", &l))
	fmt.Println("l is -->", l)

	m := l
	m[2] = 222
	fmt.Println(fmt.Sprintf("m at -->%p", m))
	fmt.Println(fmt.Sprintf("m p at -->%p", &m))
	fmt.Println("m is -->", m)
	fmt.Println("l is -->", l)

	var n MyType = new(int)
	fmt.Println(fmt.Sprintf("n at -->%p", n))

	fmt.Println("n type-->", reflect.TypeOf(n))
	fmt.Println("m type-->", reflect.TypeOf(m))
}

l at -->0xc000066150
l p at -->0xc00000e028
l is --> map[1:111]
m at -->0xc000066150
m p at -->0xc00000e038
m is --> map[1:111 2:222]
l is --> map[1:111 2:222]
n at -->0xc00001e120
n type--> main.MyType
m type--> map[int]int

l和m可以被%p接收本身就说明make出来的变量是指针类型。这就类似于n(main.MyType类型)。虽然看起来不是指针类型(不带*符号),但实际上只是指针类型的等价别名而已!

可以看到虽然l和m本身的地址是不同的,但是他们指向的地址是相同的!说明他们实际引用的内存空间是相同的,这就解释了i和j能相互影响的问题!

相关标签: Golang