一、引言

go语言相比于Java多了指针的使用,因此在变量的使用上,更加灵活。但是巧妙的使用能避免内存的浪费!

二、三种方式
	var c1 *Config
	var c2 = &Config{}
	c3 := new(Config)
三、区别

上面的三种方式返回的都是指针,但是又有什么区别呢?

fmt.Println(reflect.TypeOf(c1),reflect.TypeOf(c2),reflect.TypeOf(c3))

打印结果

*main.Config *main.Config *main.Config

可以见的三种返回的值都是指针,好像并没有什么不同,那接下来我们进行赋值,看看会不会有什么不同?

	c1.path = "./go"
	c2.path = "./go"
	c3.path = "./go"

此时运行你会发现,c1赋值的时候竟然报错了,c2,c3输出的结果一样
那么c1为什么报错呢。我们不赋值来打印一下c1,c2,c3

<nil>
&{ 0}
&{ 0}

c1为空指针,c2,c2都被赋予初始值
此时结果就已经很明显了,c1只是声明一个指针。c2通过结构体{}赋予初始值。c3通过new也进行了初始值的赋值。因此c1不能赋值,c2,c3可以进行赋值。

那么c1,c2c3究竟在什么时候使用呢?

四、不同场景下的使用

我们先定义一个函数

	func (c *Config) Path() string {
		
		if c == nil {
			return "/usr/home"
		}
		return "/go"
	}

然后我们分别用c1,c2,c3调用,分析返回值有什么不同?

	/home
	./go
	./go

发现都能调用成功,且返回值不同,c2和c3貌似一直都没有区别,那我们就重点分析c1和c3

还是回到那个问题,什么时候用c1和什么时候用c3呢?

五、需要用到变量进行赋值的时候使用

刚刚我们定义了一个函数,在里面我们并没有使用结构体里面的变量,我们重新写这个函数

func (c *Config) Path() string {

	if c == nil {
		return "/home"
	}
	c.path = "./go"
	return c.path
}

这个时候如果我们没加c==nil的条件判断,此时用c1调用就会报空指针错误。
在函数中我们使用了c.path,因此我们常常在声明的时候使用c3:=new(Config)
如果在函数中不适用,则完全可以使用c1,这种方式只声明一个指针,更加节省空间。

六、懒加载使用

每次我们定义指针也不知道在接下来的函数中是否使用,或则上游传递给调用下游的函数,也不知道下游是否使用,遇到这种情况我们怎么办呢?是全部用new给结构体赋上初始值,还是使用一个单纯的指针呢,如果单纯的使用指针,报空指针这个锅谁来背?

这个时候我们就可以使用懒加载。既节省内存,又不担心报错。

func (c *Config) Path() string {

	if c == nil {
		c = &Config{}
	}
	c.path = "./go"
	return c.path
}

经过我们简单的改动,上面代码c1,c2,c3结果相同,也不会报空指针异常。
其中c==nil的判断,然后赋值就是懒加载的过程。
懒加载:当我们使用的时候才给他开辟内存空间,而不是一开始就给他开辟一块内存,并赋予初始值。