写在前面
以前在写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)
}
很明显出现报错了,查看报错原因是空指针,实际上操作的时候只初始化了一个空的指针,不管你怎么赋值都没用
也许我们可以修改一下代码
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)
}
运行成功,这也是因为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
可以看到第二种情况是确实分配了空间,而第一种情况只是空指针,所以如果想要一个空的结构体的地址,可以使用第二种方法