当前位置: 首页 > news >正文

golang结构体与指针类型

结构体与指针类型

指针类型字段

具名字段

举例

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

结果

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

注意事项

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

//方法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)

匿名字段

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

//报错:string redeclared
type Student struct{
    *string
    string
}

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

举例

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

结果

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

指针类型嵌套结构体

具名结构体

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

举例

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

结果

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

匿名结构体

和匿名字段一样

type Animal struct{
    name string 
}
type Dog struct{
    *Animal
}

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

注意

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

举例

//匿名结构体
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)

}

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

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

结果

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

接收者类型

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

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

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

举例

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

调用

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

结果

接收者为值类型时,方法内的实例值为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.方法的接收者是指针还是值不是由其调用者决定的,而是由方法本身决定的,如果方法的接收者为指针,方法就会自动取调用者的指针。

var day struct_knowledge.Day
day.Order = 1

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

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

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

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

相关文章:

  • ARM day2
  • protobuf为什么快
  • 基于ssm的微博网站(全套)
  • 流量分析2
  • VectorBT:使用PyTorch+LSTM训练和回测股票模型 进阶二
  • 核心知识——论文详解
  • 力扣DAY27 | 热100 | 合并两个有序链表
  • 万字C++STL——vector模拟实现
  • LeetCode 524 通过删除字母匹配到字典里最长单词
  • 虚幻引擎设置复杂碰撞体
  • docker使用uv安装依赖
  • 探索Halo:不止是博客,更是创作新宇宙
  • VUE3项目VITE打包优化
  • [BalticOI 2009] Radio Transmission 无线传输
  • 项目日记 -云备份 -服务端数据管理模块
  • Qt Concurrent 并发 Map 和 Map-Reduce
  • Chat2DB:一款强大的数据库管理工具,AI助力高效查询与分析
  • 如何使用Python爬虫按关键字搜索1688商品?
  • SQL HAVING 1 的用法解析
  • 鸿蒙开发:父组件如何调用子组件中的方法?
  • 女生“生理期请病假要脱裤子证明”?高校回应:视频经处理后有失真等问题
  • 奥古斯都时代的历史学家李维
  • 经济日报评外卖平台被约谈:行业竞争不能背离服务本质
  • 市场监管总局召开平台企业支持个体工商户发展座谈会
  • 中科飞测将投资超10亿元,在上海张江成立第二总部
  • 京东CEO许冉:外卖日单量接近2000万单,看到外卖对平台拉动和转化效应