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 小时前
序列化与反序列化:把 C++ 对象可靠地保存下来
开发语言·数据库·c++·php
ragnwang3 小时前
Notes: Python pybind11 import C++ module
开发语言·c++·python
Wang's Blog3 小时前
Java 项目实战: 交友项目 - 搜索附近的功能概述和分析
java·开发语言·交友
骑着蜗牛撵大象3273 小时前
大模型 Agent 的沙箱护栏:gVisor 隔离、出口白名单与审计闭环
java·开发语言·jvm·安全·agent沙箱·gvisor·microvm
IMPYLH3 小时前
Linux 的时间为什么会被拨回去
linux·运维·服务器
2601_963282774 小时前
寒地窄带通信工程实践:黑龙江哈尔滨对讲机选型、组网与合规运维总结
运维·服务器·网络
Dxy12393102164 小时前
JS DES 加密详解
开发语言·javascript·arcgis
充值内卷4 小时前
我给桌面加了个鱼缸:C++ 写后端,WebView2 里养一座 Three.js 小岛
开发语言·javascript·c++
云和数据.ChenGuang5 小时前
深入解析 Python 的 dir():从用法到 CPython 源码实现
开发语言·人工智能·python·langchain
nice_lcj5206 小时前
深入理解 Java 反射:从 Class 对象到方法调用(含性能实测 + 迷你 IOC 实战 + 面试考点)
java·开发语言·后端·面试