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)
}
相关推荐
阿里嘎多学长1 小时前
2026-04-30 GitHub 热点项目精选
开发语言·程序员·github·代码托管
j_xxx404_2 小时前
Linux:静态链接与动态链接深度解析
linux·运维·服务器·c++·人工智能
叶小鸡2 小时前
Java 篇-项目实战-苍穹外卖-笔记汇总
java·开发语言·笔记
AI人工智能+电脑小能手3 小时前
【大白话说Java面试题】【Java基础篇】第22题:HashMap 和 HashSet 有哪些区别
java·开发语言·哈希算法·散列表·hash
时空系3 小时前
第10篇:继承扩展——面向对象编程进阶 python中文编程
开发语言·python·ai编程
墨风如雪3 小时前
别被“高价建站”劝退了!我跑了多年的 WordPress 架构,一年只花 $25.7
服务器
云飞云共享云桌面4 小时前
东莞智能装备工厂数字化实践—研发部门10名SolidWorks设计共享一台云主机流畅设计
服务器·自动化·汽车·负载均衡·制造
专注API从业者4 小时前
Open Claw 京东商品监控选品实战:一键抓取、实时监控、高效选品
java·服务器·数据库
xingyuzhisuan4 小时前
稳定性考验:连续跑7天,哪家云主机不重启、不掉线?
服务器·人工智能·gpu算力
jsons14 小时前
给每台虚拟机设置独立控制台密码
linux·运维·服务器