一.结构体内存分配机制
看一个问题
定义一个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))
}