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能相互影响的问题!
上一篇: 什么是RESTful
下一篇: [转]Go里面的unsafe包详解