文章目录
- 前言
- 一、优化基于指针对象方法的内存
- 二、充当类型返回
前言
- 最近从java里偷闲出来学会go,发现当初c语言中不懂的指针,到golang里还是不是很理解,特此用此篇持续记录自己对golang指针的理解。
type Person struct {
Name string
}
type Son struct {
Sex string
}/*
Golang中的方法作用在指定的数据类型上的(即:和指定的数据类型绑定),因此自定义类型,都可以有方法,
而不仅仅是struct,比如int ,float32等都可以有方法。
*/
func (p Person) testAddress(son Son){
fmt.Printf("testAddress: son的指针地址为: %p\n",&son)
fmt.Printf("testAddress: p的指针地址为: %p\n",&p)
}func main() {
p := Person{Name: "jack"}
son := Son{Sex: "man"}
p.testAddress(son)
fmt.Printf("main: p的指针地址为: %p\n",&p)
fmt.Printf("main: son的指针地址为: %p\n",&son)}
上面的代码定义了两个结构体Person、Son,以及基于Person的一个结构体方法 testAddress,测试传入参数的地址。主协程main方法里打印传入方法前的地址,用来对比。
- 打印结果:
文章图片
文章图片
因此如果在方法中,对方法所属的结构体,或者传参的属性进行修改是不会影响到原有的参数,或者方法绑定的结构体。上代码:
func (p Person) testAddress(son Son){
p.Name = "Rin"
son.Sex = "women"
fmt.Printf("testAddress: p的名字为为: %v\n",p.Name)
fmt.Printf("testAddress: son的性别为: %v\n",son.Sex)
fmt.Printf("testAddress: son的指针地址为: %p\n",&son)
fmt.Printf("testAddress: p的指针地址为: %p\n",&p)
}func main() {
p := Person{Name: "jack"}
son := Son{Sex: "man"}
p.testAddress(son)
fmt.Printf("main: p的名字为为: %v\n",p.Name)
fmt.Printf("main: son的性别为: %v\n",son.Sex)
fmt.Printf("main: p的指针地址为: %p\n",&p)
fmt.Printf("main: son的指针地址为: %p\n",&son)}
- 输出结果:
文章图片
- 显而易见:在方法中修改后的属性,在方法后没有任何改变。 也证实了前面的结论。
- 那么如果传参换成指针呢? 继续上代码:
func (p *Person) testAddress(son *Son){
p.Name = "Rin"
son.Sex = "women"
fmt.Printf("testAddress: p的名字为为: %v\n",p.Name)
fmt.Printf("testAddress: son的性别为: %v\n",son.Sex)
fmt.Printf("testAddress: son的指针地址为: %p\n",son)
fmt.Printf("testAddress: p的指针地址为: %p\n",p)
fmt.Printf("testAddress: son的指针存储地址为: %p\n",&son)
fmt.Printf("testAddress: p的指针存储地址为: %p\n",&p)
}func main() {
p := Person{Name: "jack"}
son := Son{Sex: "man"}
p.testAddress(&son)
pPoint := &p
sonPoint := &son
fmt.Printf("main: p的名字为为: %v\n",p.Name)
fmt.Printf("main: son的性别为: %v\n",son.Sex)
fmt.Printf("main: son的指针地址为: %p\n",sonPoint)
fmt.Printf("main: p的指针地址为: %p\n",pPoint)
fmt.Printf("main: son的指针存储地址为: %p\n",&pPoint)
fmt.Printf("main: p的指针存储地址为: %p\n",&sonPoint)
}
- 输出结果:
文章图片
文章图片
因此会有种错误的修改方式:
func (p *Person) testAddress(son *Son){
p = &Person{Name: "Rin"}
fmt.Printf("testAddress: p的指针地址为: %p\n",p)
}func main() {
p := Person{Name: "jack"}
son := Son{Sex: "man"}
p.testAddress(&son)
pPoint := &p
fmt.Printf("main: p的名字为为: %v\n",p.Name)
fmt.Printf("main: p的指针地址为: %p\n",pPoint)}
这种方式相当于:
文章图片
跟原有的引用没关系了。显然输出的结果不会有变化:
文章图片
- 那么看到这里就有读者问了,那么这样如果想直接改变参数值,只能通过对参数内的变量一一赋值才能修改成功呢?显然这对复杂对象是件繁琐的事所以有这样一种很简单方式也可以将对象赋给传入参数:
func (p *Person) testAddress(son *Son){
*p = *&Person{Name: "Rin"}
fmt.Printf("testAddress: p的指针地址为: %p\n",p)
}
只要再将双方指针都指向内容就可以轻松再做到对整个内容的copy了~
- 也因此可以得到优化方法:将指针传入方法函数内部时,不用再去开辟一个新的内存去逐一拷贝元素,而是直接传入一个引向原有对象的地址指针。所以有的时候在传入的对象很复杂的时候,这是一个的内存优化手段。
type Woman struct {
Name string
}
type Son struct {
Sex string
}
试想我们想编造一个这样的函数:传进一个字符串sex当sex为“man”的话返回一个son,如果不为“man”的话返回一个空:
func (w *Woman) makeSon(sex string) (Son){
if sex != "man" {
return Son{Sex: "sex is man"}
}else {
return nil
}
}
显然这段代码是会报错的:
文章图片
而这在java是可以返回一个null的,因为null是java中所有引用类型的默认值,它可以被转化为任何类型,属于java中的一个关键字,这代表我们可以返回一个没有实例的对象,因为只是一个这个类型的引用。
- 那么我们要如何做呢?
- 方法一:利用error
让我们将上面的方法稍微改一改:
func (w *Woman) makeSon(sex string) (error,Son){
if sex != "man" {
return errors.New("women can't have a son"),Son{}
}else {
return nil,Son{Sex: "sex is man"}
}
}
main主协程,以及测试log方法:
func log(w Woman,sex string) {
err,son := w.makeSon(sex)
if err != nil {
err = fmt.Errorf("makeSon fun has error : %s", err)
fmt.Println(err.Error())
return
}else {
fmt.Println("has a son:",son)
}
}func main() {
w := Woman{Name: "Jane"} log(w,"man")
log(w,"woman")}
成功实现了这样的测试效果:
文章图片
- 方法二:利用指针进行返回
在开头中其实说到java中其实是通过null返回这个类的引用类型,那么一提到引用那就是应该联想到go中的指针了,它同样代表一个引用,指向目标的地址。
再来简单看看对nil的解释:
由此看来我们可以完全将传入参数改为指针传参,如果没有则返回一个空,接下来我们不动main函数变一变makeSon,与log方法:
- nil是一个预先声明的标识符,代表指针(pointer)、通道(channel)、函数(func)、接口(interface)、 map、切片(slice)。
- 也可以这么理解:指针、通道、函数、接口、map、切片的零值就是nil,就像布尔类型的零值是false、整型的零值是0。
func (w *Woman) makeSon(sex string) *Son {
if sex != "man" {
return &Son{Sex: "sex is man"}
}else {
return nil
}
}
func log(w Woman,sex string) {
son := w.makeSon(sex)
if son == nil {
err := fmt.Errorf("women can't have a son")
fmt.Println(err.Error())
return
}else {
fmt.Println("has a son:",son)
}
}
显然我们可以得到一个相同的结果:
文章图片
【golang|Golang指针的应用场景理解】(最后更新时间:4/18,最近其实都在接触go,但是后面就比较忙要去重回java了,此篇可能暂时不会更新了,但是还是会继续看一些关于go的书,如果觉得此篇讲的对同学有所帮助可以先收藏m住,后续会持续更新…。)
推荐阅读
- java|曝光 Java方法调用的底层原理
- java|成为架构师,我仅仅用了5年,手把手教你进阶之路
- Java|Java进阶学习之Java架构师的学习路线
- 项目总结|【高并发】秒杀系统设计方法论
- 程序人生|互联网让我的人生逆袭
- 架构|工程师如何成功转技术管理
- redis数据结构附录
- Golang笔记|【记录】go mod命令 & go.mod 文件解析
- 程序人生|C语言的主要用途以及未来发展