#首先搞清楚几个问题
1.声明:只是规定了变量的类型和名字,而没有进行内存分配。
2.定义:不仅规定了变量的类型和名字,而且进行了内存分配,也可能会对变量进行初始化。
---------------------------------------------------------------------------------------------------------------------
3.初始化:在定义之后赋值为初始化(所以在初始化时或者说前面就已经分配内存了)当对象在创建时获得了一个特定值。
4.赋值:擦除原有的值,并赋予新值。
(两者的区别其实就是在创建时给值,还是创建后给值。)
在C++中
int a; //声明并定义a
extern int a; //extern表示声明但不定义a。
extern int a = 2; //任何包含了显示初始化的声明就成了定义
int a = 2; //初始化
a = 3; //赋值
注: 在一个程序中,变量只能定义一次,却可以声明多次。
定义分配存储空间,而声明不会。定义完之后才能初始化,所以初始化的时候已经分配 内存
一.new和make区别
1.变量的声明:
var i int
var s string
变量的声明我们可以通过var关键字,然后就可以在程序中使用。当我们不指定变量的默认值时,这些变量的默认值是他们的零值,比如int类型的零值是0,string类型的零值是"",引用类型的零值是nil。
对于上述例子中的两种类型的声明,我们可以直接使用,对其进行赋值输出。但是如果我们换成引用类型呢?
2.实例描述:
/*
var x *int
*x = 8
fmt.Println(*x)
上述指针值为nil,这样直接赋值,会造成程序发生panic
*/
var p int
var x *int
x = &p
*x = 8
fmt.Println(*x)
//将已经存在的变量p,用它的指针地址 对变量x指针进行地址初始化
//这样才能正确赋值,而当上述情况,不存在变量时我们应该怎么处理呢?这就可以用到new了
//一.new篇
//new是用来进行分配内存的内置函数,应用场景:new可以对不是已经存在的变量进行赋值(引用类型除外)并内存清零
var t *int
//fmt.Println(*t)//直接进行取指针值为nil的变量值,会出现panic
fmt.Println(t) //nil
t = new(int)
fmt.Println(*t) //0
fmt.Println(t) //0xc000018098
//可以发现 初始化t其指针值为nil,nil值是不能直接赋值的,会出现panic。而当用new其变量指向新分配的类型为int的指针,其指针地址的值为0xc000018098,这个指针指向的内容的值是0
//注意:new不同指针类型其零值是不同的 实例如下:
var y *int
var z *string
var b *[5]int
y = new(int)
z = new(string)
b = new([5]int)
fmt.Println(*y, *z, *b) //0 空值 [0,0,0,0,0]
fmt.Print(y, z, b) //0xc0000180f0 0xc00004c250 &[0 0 0 0 0]
//用new进行赋值
//1 数组(arrays)赋值
av := new([5]int)
fmt.Printf("av: %p %#v \n", &av, av) //av: 0xc000074018 &[5]int{0, 0, 0, 0, 0}
(*av)[1] = 8
fmt.Printf("av: %p %#v \n", &av, av) //av: 0xc000006028 &[5]int{0, 8, 0, 0, 0}
//2 切片(slice)赋值
//ar := new([]int)
//fmt.Printf("av: %p %#v \n", &ar, ar) //av: 0xc000074018 &[]int(nil)
//(*ar)[0] = 8
//fmt.Printf("av: %p %#v \n", &ar, ar) //panic: runtime error: index out of range
//而引用类型不能用new进行初始化分配内存该怎么办呢? 这里就用到了make
//二.make篇
//make 可以进行开辟内存,并且可以初始化类型零值
var a *[]int
fmt.Printf("地址:%p,完整结构:%#v", &a, a) //地址:0xc000006038,完整结构:(*[]int)(nil)
d := make([]int, 5) //分配了内存空间,并初始化了零值
fmt.Printf("地址:%p,完整结构:%#v", &d, d) //地址:0xc000004078,完整结构:[]int{0, 0, 0, 0, 0}
c := make(map[string]string)
fmt.Printf("地址:%p,完整结构:%#v", &c, c) //地址:0xc000006040,完整结构:map[string]string{}
//make与new的结合运用
var ddd *map[string]string
fmt.Printf("ddd: %p %#v \n", &ddd, ddd) //ddd: 0xc042004028 (*map[string]string)(nil)
ddd = new(map[string]string)
fmt.Printf("ddd: %p %#v \n", &ddd, ddd) //ddd: 0xc000006028 &map[string]string(nil)
//(*ddd)["b"] = "b"
/*这里是不可以的,因为silce、map、channel类型也属于引用类型,go会给引用类型初始化为nil,nil是不能直接赋值的。并且不能用new分配内存。无法直接赋值。*/
*ddd = make(map[string]string)
(*ddd)["a"] = "a"
fmt.Printf("ddd: %p %#v \n", &ddd, ddd) //ddd: 0xc042004028 &map[string]string{"a":"a"}
总结:
共同点:都可以在堆上进行内存分配
不同点:
1.make只用于slice、map以及channel的分配内存和初始化;而new用于类型的内存分配,并且内存置为零。对于引用类型的变量,声明时不会自动初始化,还要手动分配内
存空间,否则会空指针或invalid memory address,对于值类型的声明时会自动分配内 存并初始化为零值。所以在我们编写程序的时候,就可以根据自己的需要很好的选择
了。
2.make返回的类型是 引用类型本身,而new返回的类型是指定类型的指针(指向指定类 型的内存地址)
二.为什么make不返回指针?
因为 golang其内置类型slice,map,channel 本身就是引用类型,内置结构就自带了指针,所以make不需要返回指针