Go里面的new和make

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能相互影响的问题!

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值