前言
  • 最近从java里偷闲出来学会go,发现当初c语言中不懂的指针,到golang里还是不是很理解,特此用此篇持续记录自己对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方法里打印传入方法前的地址,用来对比。

  • 打印结果:
    在这里插入图片描述

从打印结果来看,传入方法前的参数与传入方法中的参数地址是不同的,说明其实方法的传参,属于形参,如果是基本类型则复制一个相同的值,如果是引用类型(map、切片、指针…)则复制一个新的地址存储原有的对象属性的拷贝
在这里插入图片描述
因此如果在方法中,对方法所属的结构体,或者传参的属性进行修改是不会影响到原有的参数,或者方法绑定的结构体。上代码:

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)
}

这种方式相当于:
在这里插入图片描述

跟原有的引用没关系了。

  • 也因此可以得到优化方法:将指针传入方法函数内部时,不用再去开辟一个新的内存去逐一拷贝元素,而是直接传入一个引向原有对象的地址指针。所以有的时候在传入的对象很复杂的时候,这是一个的内存优化手段。