命令记不住

登陆

结构体和匿名字段

结构体和匿名字段

1.结构体

Go 语言中数组切片可以存储同一类型的数据,但在结构体中可以为不同项定义不同的数据类型。
结构体是由一系列具有不同类型的数据构成的数据集合。

  • 注意:结构体成员在定义时不能赋值
type 结构体名 struct {
	结构体成员名 数据类型 `结构体标签`
	结构体成员名 数据类型
	结构体成员名 数据类型
}
package main

import "fmt"

type Student struct {
	name  string
	age   int
	sex   string
	score int
	addr  string
}

func main() {
	//定义结构体变量
	stu := Student{"cloud", 2, "男", 100, "北京朝阳区"}
	fmt.Println(stu)
	stu.name = "x86"
	fmt.Println(stu)
	/*
		C:\Users\vSphere\go\src\day03>go run 07.go
		{cloud 2 男 100 北京朝阳区}
		{x86 2 男 100 北京朝阳区}
	*/
	fmt.Printf("%p\n", &stu)
	fmt.Println(&stu.name)
	fmt.Println(&stu.age)
	fmt.Println(&stu.sex)
	fmt.Println(&stu.score)
	fmt.Println(&stu.addr)
	//结构体的地址和第一个值的地址一样
	/*
		0xc00001c080
		0xc00001c080
		0xc00001c090
		0xc00001c098
		0xc00001c0a8
		0xc00001c0b0
	*/
}

2.空结构体

go语言中的struct是一种很重要的语法,里面可以存放各种字段,当然它里面也可以为空。这样可以用来单纯的做控制信息。

a := struct{}{}
fmt.Println(unsafe.Sizeof(a))
// 结果: 0

由于空结构体占用0字节,那么空结构体也不需要填充字节。所以空结构体组成的组合数据类型也不会占用内存空间。

type S struct {
	A struct{}
	B struct{}
}
var s S
fmt.Println(unsafe.Sizeof(s))
// 结果: 0

有一种特殊的struct{}类型的channel,它不能被写入任何数据,只有通过close()函数进行关闭操作,才能进行输出操作。struct{}类型的channel不占用任何内存。
定义:

var sig = make(chan struct{})

使用空 struct 是对内存更友好的开发方式,在 go 源代码中针对 空struct 类数据内存申请部分,返回地址都是一个固定的地址。那么就避免了可能的内存滥用。

  • 如果定义多个空结构体都指向同一个内存地址 (全局区)
package main

import (
	"fmt"
	"unsafe"
)

func main(){
	//如果定义多个空结构体都指向同一个内存地址(全局区)
	//nill := NULL{}
	null1 := struct {

	}{}

	fmt.Println(unsafe.Sizeof(null1))
	fmt.Printf("%p\n",&null1)

	null2 := struct {

	}{}
	fmt.Println(unsafe.Sizeof(null2))
	fmt.Printf("%p\n",&null2)
}
C:\Users\vSphere\go\src\day03>go run 08.go
0
0x5a6d88
0
0x5a6d88

  • 空结构体嵌套空结构体,结构体匿名字段
package main

import "fmt"

func main(){
	type S struct {
		A struct{}
		B struct{}
	}
	s := S{}
	fmt.Println(s.A)
	fmt.Println(s.B)
}
C:\Users\vSphere\go\src\day03>go run 08.go
{}
{}

3 结构体标签

  • 结构体的tag设置
type People struct {
	Name string `json:"name"`
	Age  uint   `json:"age"`
}
  • 反射

tag 是类型的一部分,主要用于通过反射获取字段的相关 tag 设置

typeOfCat := reflect.TypeOf(People{})
for i := 0; i < typeOfCat.NumField(); i++ {
	field := typeOfCat.Field(i)
	tag := field.Tag.Get("json")
	fmt.Println(tag)
}
  • 通过反射获取结构体成员
package main

import (
	"fmt"
	"reflect"
)

// 在结构体操作中  结合json时,结构体成员首字母大写
type Perple struct {
	Name string `json:"name"`
	Age  int    `json:"age"`
}

func main() {
	//通过反射获取结构体成员
	typeof := reflect.TypeOf(Perple{})
	//遍历结构体成员
	for i := 0; i < typeof.NumField(); i++ {
		field := typeof.Field(i)
		tag := field.Tag.Get("json")
		fmt.Println(tag)
	}
}

4.结构体操作

  • map中操作
package main

import "fmt"

type Student struct {
	name string
	age  int
}

func main() {
	m := make(map[int]Student)
	s1 := Student{"x86", 2}
	s2 := Student{"cloud", 3}

	m[1001] = s1

	//m[1003].name = "com" //不允许成员写单个操作,必须整体操作
	//整体操作
	m[1002] = s2
	fmt.Println(m)
	//允许读单个操作
	fmt.Println(m[1001].name, "成员读操作", m[1001].age)

}
  • 结构体操作
package main

import "fmt"

type Student struct {
	name string
	age  int
}

func main() {
	//定义结构体变量
	stu := Student{"女娲", 2}
	fmt.Println(stu)
	stu.name = "钟馗"
	stu.age = 33
	fmt.Println(stu)
}

5.匿名字段

可以通过匿名字段实现 “ 继承关系 ”,所谓的匿名字段就是将结构体名称作为另外结构体的成员。

继承后,子类可以直接使用父类的结构体成员。

type People struct {
	Name string
	Age  uint
}
type Student struct {
	//将结构体名称作为另外结构体的成员 匿名字段
	People
	Score int
}
  • 匿名字段 将结构体作为另外一个结构体成员
package main

import "fmt"

//子类和父类有共同的成员名
type Perple1 struct {
	Name string
	Age  int
}
type Student1 struct {
	//P Perple1
	//匿名字段   将结构体作为另一个结构体成员
	Perple1
	Name string
	Score int
}

func main(){
	stu :=Student1{Perple1{"x86",2},"cloud",3}
	fmt.Println(stu)

	//子类和父类有共同的成员名,在修改同名结构体成员时采用就近原则 修改本结构体信息
	stu.Name = "ops"
	fmt.Println(stu)
	//为父类结构体进行修改成员信息
	stu.Perple1.Name = "dev"
	fmt.Println(stu)
}

C:\Users\vSphere\go\src\day03>go run 10.go
{{x86 2} cloud 3}
{{x86 2} ops 3}
{{dev 2} ops 3}

推荐阅读