go中易错的结构体声明空指针问题

289 阅读2分钟

写在前面

以前在写go的时候都感觉变量声明很随意,感觉跟写py差不多,但是最近突然几种声明方式还是有区别的,而且很容易出现空指针的情况

案例分析

当给定一个这样的handle函数如何去实现呢,当然实际代码可能更复杂,但是也许你会需要一个这样的函数来处理

type Data struct {
	ID int
}

func handle(data *Data) *Data {
   // your code
}

我们来看第一种写法,很多时候我们看到指针可能会下意识的初始化指针进行操作,当然这段代码可能有点丑陋

type Data struct {
	ID int
}
func handle(data *Data) *Data {
	var a *Data
	a.ID = data.ID
	return a
}
func main() {
	var data *Data
	data.ID = 2
	a := handle(data)
	fmt.Println(a.ID)
}

image.png 很明显出现报错了,查看报错原因是空指针,实际上操作的时候只初始化了一个空的指针,不管你怎么赋值都没用


也许我们可以修改一下代码

type Data struct {
	ID int
}
func handle(data *Data) *Data {
	var a = new(Data)
	a.ID = data.ID
	return a
}
func main() {
	var data = new(Data)
	data.ID = 2
	a := handle(data)
	fmt.Println(a.ID)
}

image.png
运行成功,这也是因为new可以开辟一个新的空间,这时候就不会出现空指针问题了,此时的指针不是空的,会指向某一个空间


也许我们也可以用另一种方式,不使用指针的方式,同样也能运行成功

type Data struct {
	ID int
}

func handle(data *Data) *Data {
	var a Data
	a.ID = data.ID
	return &a
}

func main() {
	var data Data
	data.ID = 2
	a := handle(&data)
	fmt.Println(a.ID)
}

这里还有一个问题,就是handle函数里面的变量a的地址返回到main函数中,依然能够使用(在c语言中,函数内的声明的数据,不加限制的情况下,会一起被销毁,所以这里应该指向的是空??)

经过多个月的学习,我现在才补充这块的知识,这是因为函数的返回值传递指针,从而产生的内存逃逸的情况,原本在堆上分配的数据,被转移到栈上

案例分析2

现在我们继续看以下情况

package main

import "fmt"

type student struct {
	Name string
}

func main() {
	var a *student
	var b *student
	var c = &student{}
	var d = &student{}

	fmt.Printf("%p %p %p %p", a, b, c, d)
}

打印的结果为

0x0 0x0 0xc000088240 0xc000088250

可以看到第二种情况是确实分配了空间,而第一种情况只是空指针,所以如果想要一个空的结构体的地址,可以使用第二种方法