开发者学堂课程【Go 语言核心编程 - 面向对象、文件、单元测试、反射、TCP 编程:结构体内存分配机制】学习笔记,与课程紧密联系,让用户快速学习知识。
课程地址:https://developer.aliyun.com/learning/course/626/detail/9738
结构体内存分配机制
内容介绍:
一、结构体内存分配机制中的问题一
二、结构体内存分配机制中的问题二
三、结构体内存分配机制中的问题三
一、结构体内存分配机制中的问题一
我们定义一个Person结构体(包括名字,年龄)。
我们看看下面一段代码,输出什么内容?
var p1 Person
p1.Age=10
p1.Name="小明"
var p2 Person = p1
fmt.PrintIn(p2.Age)
p2.Name = "tom"
fmt.Printf("p2.Name=%v p1.Name=%v", p2.Name, p1.Name)
l 输出的结果是p1:小明、p2:Tom
1、问题分析
定义了一个person结构体变量,age字段赋值为10,Name字段赋值为小明,然后把p1交给p2,上述过程执行的时候,p1会指向一个数据空间,空间里会有名字:小明,还有一个年龄:十岁。当我们把p1交给p2的时候,进行值的拷贝,由于结构体这种数据类型默认值是拷贝的,因此p2也会有一个数据空间,p2会指向自己的数据空间,将小明还有其年龄拷贝过去。输出(p2.Age)结果为10。通过p2的结构体变量访问到它的属性,将小明改成tom,而这样的修改不会影响到p1。输出p2与p1的Name分别是 tom 和小明。
2、基本说明
变量总是存在内存中的,那么结构体变量在内存中究竟是怎样存在的?
结构体在内存中的示意图
对图示进行分析:例如写入两个人,一个是小明,一个是tom,当我们把p1交给p2的时候,会进行一次值拷贝,且他们之间的数据空间是完全独立的。
二、结构体内存分配机制中的问题二
下面一段代码,会输出什么信息:
var p1 Person
p1.Age=10
p1.Name= "小明"
var p2*Person = &p1
fmt.Println((*p2).Age)
fmt.Println(p2.Age)
p2.Name = "tom~"
fmt.Printf("p2.Name=%v p1.Name=%v \n",p2.Name, p1.Name)
fmt.Printf("p2.Name=%v p1.Name=%v \n",(*p2).Name, p1.Name)
l 输出结果为:p1、p2均为tom~
1、问题分析
创建p1结构体变量,为其年龄名字赋值,把p1变量的地址交给了p2,而p2是一个结构体指针。利用取值符,把Age取出,值为10,然后把p2的年龄也进行输出。p2是一个指针,它指向p1,所以p2的年龄也是10。通过p2修改名字,把它改成Tom。进入到chapter10,然后cd到刚才的exercise,go run main.go,运行起来,完成p2和p1的Name输出,因为此刻是指针,所以此时p1和p2 Name都应该是tom~,而若以(*.p2).Name的形式输出时,则输出结果为:
p2.Name=tom~ p1.Name =tom~
p1的地址0xc0420023e0 p2的地址0xc042004028 p2的值0xc0420023e0
测试程序
核心代码:
package main
import(
"fmt"
)
type Person struct{
Name string
Age int
}
func main(){
var p1 Person
p1.Age=10
p1.Name=“小明”
var p2 *Person = &p1//这里是关键-->将画出示意图
fmt.Print1n((*p2).Age)
fmt.Print1n(p2.Age)
p2.Name = "tom~"
fmt.Printf("p2.Name=%v p1.Name=%v \n", p2.Name,p1.Name)
fmt.Printf("p2.Name=%v p1.Name=%v \n",(*p2).Name,p1.Name)
}
2、示意图
结构体在内存中的示意图
对示意图进行分析:创建一个p1,内存里p1这个变量就会分配一个空间。
p1里面有两个字段,Name和Age,Name值给成小明,年龄给成10,p1就这样指向了其空间。关键代码表示把p1的地址交给了指针p2, 这时会有一个新的p2指向一个空间,但这个空间里面存的是p1结构体的地址,而p1结构体的地址可以通过fmt.Printf("p1的地址%p\n",&p1)进行查看,这个地址会指向p1结构体的空间。p2本身的地址可以通过fmt.Printf("p2的地址%p\n",&p)进行查看,同时可对刚才的结果通过fmt.Printf("p2的值%p\n",&p)进行判断,应恰好等于p1的地址,即可证明了p2指针的存放地址指向p1结构体的数据空间。通过p1的地址找到了其数据空间,再找到Age,打印出10,同样p2.Name打印出小明。这句话之所以可以这么用,是因为go的设计者做了简化处理,为了大家使用方便,支持指针直接去访问它的字段。p2.name=tom,其实是通过指针的地址找到了它指向的数据空间,然后找到里面的Name字段,然后将值改成了tom~。通过p2去改变内容,p1就会发生变化,这是因为他们共享一份数据空间,所以最终访问结果都是tom~,同样的道理用(*p2)去访问,最终结果也是tom~。
三、结构体内存分配机制中的问题三
看下面代码,并分析原因
var p1 Person
p1.Age=10
p1.Name= "小明”
var p2*Person = &p1
fmt.PrintIn(*p2.Age)
//能不能这样写?
结果为:不能按题目所写
问题分析
为结构体p1的两个字段赋值,然后把p1的地址交给p2指针,题目中fmt.Println(*p2.Age)与原先所写的唯一的区别就是没有把*p2用括号括起来,而这样做是不对的,这是因为.的运算优先级*高。