持续创作,加速成长!这是我参与「掘金日新计划 · 10 月更文挑战」的第36天,点击查看活动详情
一、结构体的内存布局
案例演示
package main
import "fmt"
type Person struct {
Name string
Age int
}
func main() {
var p1 Person
p1.Name = "aliy"
p1.Age = 20
var p2 *Person = &p1
// p1 = {aliy 20}
// p2 = {aliy 20}
fmt.Printf("p1 = %v\n", p1)
fmt.Printf("p2 = %v\n", *p2)
p2.Name = "Bob"
fmt.Printf("p2.Name = %v, p1.Name = %v \n", p2.Name, p1.Name)
fmt.Printf("p2.Name = %v\n", (*p2).Name) // 等同于 p2.Name
// 查看 p1 p2的地址
// p1 add = 0xc000004078
// p2 add = 0xc000006028
fmt.Printf("p1 add = %p\n", &p1)
fmt.Printf("p2 add = %p\n", &p2)
// p2的值
// p2 = 0xc000004078
fmt.Printf("p2 = %p\n", p2)
}
二、结构体使用细节
2.1 结构体的所有字段在内存中是连续的
package main
import "fmt"
// 定义两个结构体
type Point struct {
x int
y int
}
type Rect struct {
leftUp, rightDown Point
}
// 指针类型
type Rect2 struct {
leftUp, rightDown *Point
}
func main() {
r1 := Rect{Point{1, 2}, Point{3, 4}}
// 查看内存分布
// r1有四个int类型数据,在内存中是连续分布的
// r1.leftUp.x = 0xc0000101e0
// r1.leftUp.x = 0xc0000101e8
// r1.rightDown.x = 0xc0000101f0
// r1.rightDown.x = 0xc0000101f8
fmt.Printf("r1.leftUp.x = %p\n", &r1.leftUp.x)
fmt.Printf("r1.leftUp.x = %p\n", &r1.leftUp.y)
fmt.Printf("r1.rightDown.x = %p\n", &r1.rightDown.x)
fmt.Printf("r1.rightDown.x = %p\n", &r1.rightDown.y)
fmt.Println()
// r2有2个 * Point 类型地址也是连续的
// 但是指向的地址不一定是连续的
r2 := Rect2{&Point{10, 20}, &Point{30, 40}}
fmt.Printf("r2.leftUp = %p\n", &r2.leftUp)
// fmt.Printf("r2.leftUp.x = %p\n", &r2.leftUp.y)
fmt.Printf("r2.rightDown = %p\n", &r2.rightDown)
// fmt.Printf("r2.rightDown.x = %p\n", &r2.rightDown.y)
// 他们指向的地址不一定是连续的
fmt.Printf("r2.leftUp 指向地址 = %p\n", r2.leftUp)
// fmt.Printf("r2.leftUp.x = %p\n", &r2.leftUp.y)
fmt.Printf("r2.rightDown 指向地址 = %p\n", r2.rightDown)
}
2.2 结构体是用户单独定义的类型,和其他类型进行转换时需要有完全相同的字段(名字,个数和类型)
package main
import "fmt"
type A struct {
Num int
}
type B struct {
Num int
}
func main() {
// struct结构体类型转换
var a A
var b B
// a = {0}
// b = {0}
fmt.Println("a = ", a)
fmt.Println("b = ", b)
//进行转换,结构体的字段,名字,类型要完全一样
// 将b类型强转为a
a = A(b)
// convert a = {0}
fmt.Println("convert a = ", a)
}
2.3 结构体进行type重新定义(取别名),Golang认为是新数据类型,但是科研相互强转
package main
import "fmt"
type Student struct {
Name string
Age int
}
type Stu Student
func main() {
var stu1 Student
var stu2 Stu
stu2 = Stu(stu1)
// stu1 = main.Student
// stu2 = main.Stu
fmt.Printf("stu1 = %T\n", stu1)
fmt.Printf("stu2 = %T", stu2)
}
2.4 struct的每个字段上可以写一个tag,该tag可以通过反射机制获取,常见的使用场景就是序列号和反序列化
2.4.1 案例
package main
import (
"encoding/json"
"fmt"
)
type Monster struct {
Name string `json:"name"`
Age int `json:"age"`
Skill string `json:"skill"`
}
func main() {
// 创建一个Monster变量
monster := Monster{"牛牛", 288, "牛牛冲击"}
// 将monster变量序列化成json格式的字符串
// 返回的是[]byte
jsonMonster, err := json.Marshal(monster)
if err != nil {
fmt.Println("Json处理错误 ", err)
}
// jsonMonster = {"name":"牛牛","age":288,"skill":"牛牛冲击"}
fmt.Println("jsonMonster = ", string(jsonMonster))
}