[go学习笔记.第十章.面向对象编程] 3.结构体内存分配机制以及注意事项和使用细节

一.结构体内存分配机制

看一个问题

        定义一个Person结构体(包括名字,年龄),看下面代码,输出什么

        

var p1 Person
p1.Name = "小明"
p1.Age = 12

var p2 Person = p1
fmt.Println(p2.Age)

p2.Name = "小红"
fmt.Printf("p2.Name = %v, p1.Name = %v \n", p2.Name, p1.Name)

上面代码输出:

        p2.Name = 小红,p1.Name = 小明

思考: 

        变量总是存在内存中的,那么结构体变量在内存中是怎样存在的?

图示说明:

 代码:

package main

import(
    "fmt"
)

type Person struct{
     Name string
     Age int
}

func main(){
    var p1 Person
    p1.Age = 10
    p1.Name = "jack"
    var p2 *Person = &p1    //关键点,画图说明

    fmt.Println((*p2).Age)    //10
    fmt.Println(p2.Age)    //10
    p2.Name = "tom"
    fmt.Printf("p2.Name=%v, p1.Name=%v\n", p2.Name, p1.Name)    // tom tom
    fmt.Printf("p2.Name=%v, p1.Name=%v\n", (*p2).Name, p1.Name) // tom tom

    fmt.Printf("p1的地址:%p\n", &p1)   //p1的地址:0xc000004078
    fmt.Printf("p2的地址:%p,p2对应的值:%p\n", &p2,p2)//p2的地址:0xc000006028,p2对应的值:0xc000004078
}

 对上述代码画图说明:

分析下面代码

var p1 Person
p1.Age = 10
p1.Name = "小明"

var p2 *Person = p1
fmt.Println(*p2.Age)

 能不能这样写?

        不能,会报错,原因: . 的运算符优先级比*高

二.注意事项和使用细节

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,在内存中是连续分配的
    fmt.Printf("r1.leftUp.x的地址:%p r1.leftUp.y的地址:%p r1.rightDown.x的地址:%p r1.rightDown.x的地址:%p \n",
    &r1.leftUp.x,&r1.leftUp.y,&r1.rightDown.x,&r1.rightDown.y)
    //相差8
    //r1.leftUp.x的地址:0xc0000101c0 r1.leftUp.y的地址:0xc0000101c8
    //r1.rightDown.x的地址:0xc0000101d0 r1.rightDown.x的地址:0xc0000101d0 

    //r2有两个 *Point类型,这两个*Point类型本身地址也是连续的
    //但是 他们指向的地址不一定是连续的
    r2 := Rect2{&Point{10, 20}, &Point{30, 40}}
    fmt.Printf("r2.leftUp本身的地址:%p r2.本身的地址:%p \n",
    &r2.leftUp,&r2.rightDown)
    //r2.leftUp本身的地址:0xc000048230 r2.本身的地址:0xc000048238
    //他们指向的地址不一定是连续的,这个要看系统当时的分配是怎样的
    fmt.Printf("r2.leftUp指向的地址:%p r2.指向的地址:%p \n",
    r2.leftUp,r2.rightDown)
}

2.结构体是用户单独定义的类型,和其它类型进行转换时需要有完全相同的字段(名称,个数和类型) 

package main

import(
    "fmt"
)

//结构体
type B struct{
    Num int
}

//结构体
type A struct{
    Num int
}

func main(){
    var a A 
    var b B 
    fmt.Printf("a = %v, b = %v \n", a, b)   // a = {0}, b = {0}
    a = A(b)    //可以转换,要求: 结构体的字段要完全一样(名字,个数,类型)
}

3.结构体进行type重新定义(相当于取别名),go认为是新的数据类型,但是相互可以强转

package main

import "fmt"

type Student struct {
    Name string
    Age int
}

type Stu Student

func main() {
    var stu1 Student
    var stu2 Stu
    stu2 = stu1
    //正确吗? 
    //回答: 错误,可以这样修改 stu2 = Stu(stu1) // ok
    fmt.Println(stu1, stu2)
}

package main

import "fmt"

type integer int

func main() {
    var i integer = 10
    var j int = 20
    j = i
    //正确吗? 
    //回答: 错误,可以这样修改: j = int(i) // ok
    fmt.Println(i, j)
}

4.struct的每个字段上,可以写上一个tag,该tag可以通过反射机制获取,常见的使用场景就是序列化和反序列化

package main

import(
    "fmt"
    "encoding/json"
)

//结构体
type Monster struct{
    Name string `json:"name"`   // `json:"name"` 就是struct tag
    Age int `json:"age"`
    Skill string `json:"skill"`
}

func main(){
    //创建一个monster
    monster := Monster{"小龙女", 12, "三叉戟"}
    //将monster变量序列化为json格式字串
    // json.Marshal()中使用了反射
    jsonStr, err := json.Marshal(monster)
    if err != nil {
        fmt.Println("json 处理错误", err)
    }
    //{"name":"小龙女","age":12,"skill":"三叉戟"}
    fmt.Println(string(jsonStr))
}

[上一节][go学习笔记.第十章.面向对象编程] 2.结构体的声明和使用,以及创建结构体实例的四种方式 

[下一节][go学习笔记.第十章.面向对象编程] 4.方法介绍,使用,调用,传参机制和方法的声明,定义剖析以及注意事项 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值