golang:var、new、make区别及使用

new 分配;make 初始化
new(T) 返回 *T 指向一个零值 T
make(T) 返回初始化后的 T,只能用于 slice,map,channel。

先整明白go里面的几大变量“**类型**”(不严谨,只是个人在使用的时候常用到的结构的一个划分)
(1) 值类型 基本类型 +  struct 
(2) 引用类型:主要是map, slice,chan 这三个引用(make创建内存的)
(3)指针类型:那就多了, *int64, *struct…

var 声明

值类型:分配内存空间,并赋该类型的零值

引用类型:不会分配内存,默认就是nil

例子:

//值类型
var i int
fmt.Println(i) //i=0



//引用类型
var j *int  
fmt.Println(j)  
*j = 10  //invalid memory address or nil pointer dereference

//也就是说,空指针还没有内存分配,是不能使用的。

new 分配 

func new(Type) *Type
new(T) 返回 *T 指向一个零值 T   返回指针类型(一般不用)

值类型和引用类型:分配内存空间并赋该类型的零值,返回一个指向该类型内存地址的指针

实际在工程使用中,通常是直接声明指针使用,不需要new操作

例子:

var j *int  
j = new(int)  // 让j里面的内容指向一块分配好的内存地址,地址里面设置int的零值:0
fmt.Println(j)   
fmt.Println(*j)  
*j = 10  
fmt.Println(*j)

golang:var、new、make区别及使用_第1张图片

make 初始化

func make(t Type, size ...IntegerType) Type
make(T) 返回初始化后的 T   返回引用类型

make用于map, slice,chan 的内存创建,因为他们三个是引用类型,直接返回这三个类型本身(引用类型),不赋零值

例子:

var c chan int //声明管道类型变量c,此时c还是nil,不可用;
fmt.Printf("%#v \n",c) //(chan int)(nil)
c = make(chan int)
fmt.Printf("%#v", c) //(chan int)(0xc000062060)

声明管道类型变量c,此时c还是nil,不可用;
通过make来分配内存并初始化,c就获得了内存可以使用了。

 make(T, args) 返回的值通过函数传递参数之后可以直接修改,即 map,slice,channel 通过函数穿参之后在函数内部修改将影响函数外部的值

func modifySlice(s []int) {
    s[0] = 1
}

s2 := make([]int, 3)
fmt.Printf("%#v", s2) //[]int{0, 0, 0}
modifySlice(s2)
fmt.Printf("%#v", s2) //[]int{1, 0, 0}

 make(T, args) 返回的是引用类型,在函数内部可以直接更改原始值,对 map 和 channel 也是如此。

func modifyMap(m map[int]string) {
    m[0] = "string"
}

func modifyChan(c chan string) {
    c <- "string"
}

m2 := make(map[int]string)
if m2 == nil {
    fmt.Printf("m2 is nil --> %#v \n ", m2) 
} else {
    fmt.Printf("m2 is not nill --> %#v \n ", m2) //map[int]string{}
}

modifyMap(m2)
fmt.Printf("m2 is not nill --> %#v \n ", m2) // map[int]string{0:"string"}


c2 := make(chan string)
if c2 == nil {
    fmt.Printf("c2 is nil --> %#v \n ", c2)
} else {
    fmt.Printf("c2 is not nill --> %#v \n ", c2)
}

go modifyChan(c2)
fmt.Printf("c2 is not nill --> %#v ", <-c2) //"string"

很少需要使用 new

以下代码演示了 struct 初始化的过程,可以说明不使用 new 一样可以完成 struct 的初始化工作。

type Foo struct {
    name string
    age  int
}

//声明初始化
var foo1 Foo
fmt.Printf("foo1 --> %#v\n ", foo1) //main.Foo{age:0, name:""}
foo1.age = 1
fmt.Println(foo1.age)


//struct literal 初始化
foo2 := Foo{}
fmt.Printf("foo2 --> %#v\n ", foo2) //main.Foo{age:0, name:""}
foo2.age = 2
fmt.Println(foo2.age)


//指针初始化
foo3 := &Foo{}
fmt.Printf("foo3 --> %#v\n ", foo3) //&main.Foo{age:0, name:""}
foo3.age = 3
fmt.Println(foo3.age)


//new 初始化
foo4 := new(Foo)
fmt.Printf("foo4 --> %#v\n ", foo4) //&main.Foo{age:0, name:""}
foo4.age = 4
fmt.Println(foo4.age)


//声明指针并用 new 初始化
var foo5 *Foo = new(Foo)
fmt.Printf("foo5 --> %#v\n ", foo5) //&main.Foo{age:0, name:""}
foo5.age = 5
fmt.Println(foo5.age)

foo1 和 foo2 是同样的类型,都是 Foo 类型的值,

foo1 是通过 var 声明,Foo 的 filed 自动初始化为每个类型的零值,

foo2 是通过字面量的完成初始化。

foo3,foo4 和 foo5 是一样的类型,都是 Foo 的指针 Foo。*但是所有 foo 都可以直接使用 Foo 的 filed,读取或修改,为什么?

如果 x 是可寻址的,&x 的 filed 集合包含 m,x.m 和 (&x).m 是等同的go 自动做转换,也就是 foo1.age 和 foo3.age 调用是等价的,go 在下面自动做了转换。

因而可以直接使用 struct literal 的方式创建对象,能达到和 new 创建是一样的情况而不需要使用 new。

你可能感兴趣的:(Go,golang)