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)
}
相关推荐
努力学习的小廉23 分钟前
【C++】 —— 笔试刷题day_9
开发语言·c++·代理模式
Yhame.24 分钟前
【 C 语言实现顺序表的基本操作】(数据结构)
c语言·开发语言·数据结构
eqwaak028 分钟前
京东商品爬虫技术解析:基于Selenium的自动化数据采集实战
开发语言·人工智能·爬虫·python·selenium·自动化
速盾cdn43 分钟前
速盾:Python可以用高防CDN吗?
开发语言·网络·python
Min_小明1 小时前
CMake 简单使用总结
android·开发语言·算法
渗透测试老鸟-九青1 小时前
记一次常规的网络安全渗透测试
运维·服务器·安全·web安全·区块链·智能合约
demonlg01121 小时前
Go 语言标准库中strings和strconv详细功能介绍与示例
开发语言·后端·云原生·golang
__XYZ1 小时前
Vala 编程语言教程-继承
c语言·开发语言·c++·c#
森焱森2 小时前
格雷码、汉明码,CRC校验的区别
服务器·c语言·网络·人工智能·算法
rainFFrain2 小时前
对锁进行封装
linux·运维·服务器·vscode