golang结构体与指针类型

结构体与指针类型

指针类型字段

具名字段

举例

go 复制代码
package struct_knowledge

import "fmt"

//结构体字段为指针类型
func StructWithPoint(){
	type Student struct{
		name *string
	}

	var lisa Student
	fmt.Printf("赋值前,Student的实例的值%#v\n",lisa)

	//错误的赋值方法
	//报错:panic: runtime error: invalid memory address or nil pointer dereference
	// *lisa.name = "hello"


	//正确的赋值方法1
	name := "lisa"
	lisa.name = &name
	fmt.Printf("赋值后,Student的实例的值%#v\n",lisa)
    
	//正确赋值方法2
	//先分配内存再赋值
	lisa.name = new(string)
	*lisa.name = "hello"

	fmt.Printf("赋值后,Student的实例的值%#v\n",lisa)
}

结果

sh 复制代码
赋值前,Student的实例的值struct_knowledge.Student{name:(*string)(nil)}
赋值后,Student的实例的值struct_knowledge.Student{name:(*string)(0xc000186050)}
赋值后,Student的实例的值struct_knowledge.Student{name:(*string)(0xc000186060)}

注意事项

一定要注意指针的内存分配,没有分配内存的指针不能赋值。

go 复制代码
//方法1:这种是在栈上分配内存
name := "lisa"
lisa.name = &name
fmt.Printf("赋值后,Student的实例的值%#v\n",lisa)

//方法2:在堆上赋值
//先分配内存再赋值
lisa.name = new(string)
*lisa.name = "hello"
fmt.Printf("赋值后,Student的实例的值%#v\n",lisa)

匿名字段

匿名指针类型字段,实际上字段名就是去除*号的类型名,举例

go 复制代码
//报错:string redeclared
type Student struct{
    *string
    string
}

//实际上等价于
type Student struct{
    //出现了同名字段所以报错
    string *string
    string string
}

举例

go 复制代码
package struct_knowledge

import "fmt"

//结构体字段为指针类型
func StructWithPoint(){
	type Student struct{
		*string
	}

	var lisa Student
	fmt.Printf("赋值前,Student的实例的值%#v\n",lisa)

	//错误的赋值方法
	//报错:panic: runtime error: invalid memory address or nil pointer dereference
	// *lisa.string = "hello"


	//正确的赋值方法1
	name := "lisa"
	lisa.string = &name
	fmt.Printf("赋值后,Student的实例的值%#v\n",lisa)
    
	//正确赋值方法2
	//先分配内存再赋值
	lisa.string = new(string)
	*lisa.string = "hello"

	fmt.Printf("赋值后,Student的实例的值%#v\n",lisa)
}

结果

sh 复制代码
赋值前,Student的实例的值struct_knowledge.Student{name:(*string)(nil)}
赋值后,Student的实例的值struct_knowledge.Student{name:(*string)(0xc0000140a0)}
赋值后,Student的实例的值struct_knowledge.Student{name:(*string)(0xc0000140b0)}

指针类型嵌套结构体

具名结构体

和普通字段的处理情况一样,举例

举例

go 复制代码
package struct_knowledge
import "fmt"
//结构体指针
func StructWithPoint1(){
	type Animal struct{
		name string 
	}
	type Dog struct{
		Anl *Animal
	}

	var dog Dog
	/*
	报错:
		panic: runtime error: invalid memory address or nil pointer dereference
		[signal SIGSEGV: segmentation violation code=0x1 addr=0x8 pc=0x490830]
	*/
	// dog.Anl.name = "duby"
	// fmt.Printf("赋值后,dog的实例的值%#v\n",dog)

	//方法1:先分配内存
	dog.Anl = &Animal{}
	dog.Anl.name = "duby"
	fmt.Printf("赋值后,dog的实例的值%#v\n",dog)

	//方法2:使用new方法
	dog.Anl = new(Animal)
	dog.Anl.name = "duby"
	fmt.Printf("赋值后,dog的实例的值%#v\n",dog)
}

结果

sh 复制代码
赋值后,dog的实例的值struct_knowledge.Dog{Anl:(*struct_knowledge.Animal)(0xc0000140a0)}
赋值后,dog的实例的值struct_knowledge.Dog{Anl:(*struct_knowledge.Animal)(0xc0000140b0)}

匿名结构体

和匿名字段一样

go 复制代码
type Animal struct{
    name string 
}
type Dog struct{
    *Animal
}

//等价于
type Dog struct{
    Animal *Animal
}

注意

当使用指针类型的匿名结构体后,普通匿名结构体的特性就失去了,

举例

go 复制代码
//匿名结构体
func StructWithPoint2(){
	type Animal struct{
		name string 
	}
	type Dog struct{
		*Animal
	}

	var dog Dog

	/*
		报错:
		panic: runtime error: invalid memory address or nil pointer dereference
		[signal SIGSEGV: segmentation violation code=0x1 addr=0x8 pc=0x490830]
	*/
	// dog.name = "Mao"
	// fmt.Printf("赋值后,dog的实例的值%#v\n",dog)

	//指针都需要先分配
	nick := &Animal{name:"Mao"}
	dog = Dog{
		nick,
	}
	fmt.Printf("赋值后,dog的实例的值%#v\n",dog)

	//也可以采用具名结构体一样的处理方式,例如
	dog.Animal = new(Animal)
	dog.Animal.name = "duby"
	fmt.Printf("赋值后,dog的实例的值%#v\n",dog)

}

对于普通匿名结构体,我们可以用顶层结构体名.字段名来访问嵌套结构体。

但是指针类型的嵌套结构体会报指针未分配内存的问题,所以我们我们必须给指针类型的结构体分配内存。

结果

sh 复制代码
赋值后,dog的实例的值struct_knowledge.Dog{Animal:(*struct_knowledge.Animal)(0xc0000140a0)}
赋值后,dog的实例的值struct_knowledge.Dog{Animal:(*struct_knowledge.Animal)(0xc0000140b0)}

接收者类型

结构体的方法的接收者可以为指针也可以为值。

由于结构体是值类型的,所以当方法接收者使用的是值时,方法内的操作与外部无关;

当方法接收者是指针时,方法内的操作会影响到外部的原始变量。

举例

go 复制代码
package struct_knowledge

import "fmt"

type Day struct{
	Name string 
	Order int 
}

func (d Day) ChangeVal(){
	if d.Order==1 {
		d.Name = "星期一"
	}else{
		d.Name = "未知"
	}

	fmt.Printf("接收者为值类型时,方法内的实例值为%#v\n",d)
}

func (d *Day) ChangeValByPorint(){
	if d.Order==1 {
		d.Name = "星期一"
	}else{
		d.Name = "未知"
	}

	fmt.Printf("接收者为值类型时,方法内的实例值为%#v\n",d)
}

调用

go 复制代码
package main

import (
	"fmt"
	"go_learn/struct_knowledge"
)
func main(){
    var day struct_knowledge.Day
	day.Order = 1
	day.ChangeVal()
	fmt.Printf("接收者为值类型时,方法外的实例值为%#v\n",day)

	day.ChangeValByPorint()
	fmt.Printf("接收者为指针类型时,方法外的实例值为%#v\n",day)
}

结果

sh 复制代码
接收者为值类型时,方法内的实例值为struct_knowledge.Day{Name:"星期一", Order:1}
接收者为值类型时,方法外的实例值为struct_knowledge.Day{Name:"", Order:1}
接收者为值类型时,方法内的实例值为&struct_knowledge.Day{Name:"星期一", Order:1}
接收者为指针类型时,方法外的实例值为struct_knowledge.Day{Name:"星期一", Order:1}

理解

1.方法的接收者是指针还是值不是由其调用者决定的,而是由方法本身决定的,如果方法的接收者为指针,方法就会自动取调用者的指针。

go 复制代码
var day struct_knowledge.Day
day.Order = 1

//调用者都是 Day实例
// changeVal这个方法使用的是实例的值
day.ChangeVal()
//ChangeValByPorint这个方法使用的是实例的指针
day.ChangeValByPorint()

2.结构体指针问题:结构体实例指针的使用形式和其值的使用形式是一样的,所以一定要明确使用的是值还是指针。

golang为了美观,将数组和结构体实例指针前的*去除了。

go 复制代码
func (d Day) ChangeVal(){
	if d.Order==1 {
		d.Name = "星期一"
	}else{
		d.Name = "未知"
	}

	fmt.Printf("接收者为值类型时,方法内的实例值为%#v\n",d)
}

func (d *Day) ChangeValByPorint(){
	if d.Order==1 {
		d.Name = "星期一"
	}else{
		d.Name = "未知"
	}

	fmt.Printf("接收者为值类型时,方法内的实例值为%#v\n",d)
}
相关推荐
码上淘金3 分钟前
【Python】Python常用控制结构详解:条件判断、遍历与循环控制
开发语言·python
Brilliant Nemo5 分钟前
四、SpringMVC实战:构建高效表述层框架
开发语言·python
格林威2 小时前
Baumer工业相机堡盟工业相机的工业视觉中为什么偏爱“黑白相机”
开发语言·c++·人工智能·数码相机·计算机视觉
橙子199110162 小时前
在 Kotlin 中什么是委托属性,简要说说其使用场景和原理
android·开发语言·kotlin
androidwork2 小时前
Kotlin Android LeakCanary内存泄漏检测实战
android·开发语言·kotlin
学地理的小胖砸3 小时前
【Python 基础语法】
开发语言·python
菜菜why3 小时前
AutoDL租用服务器教程
服务器
IT专业服务商3 小时前
联想 SR550 服务器,配置 RAID 5教程!
运维·服务器·windows·microsoft·硬件架构
DanB244 小时前
Java笔记4
java·开发语言·笔记
Dddle14 小时前
C++:this指针
java·c语言·开发语言·c++