【一Go到底】第三十六天---结构体的内存布局及使用细节

127 阅读2分钟

持续创作,加速成长!这是我参与「掘金日新计划 · 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))
}