go-结构体内存布局

方式一:通过 var 声明结构体

在 Go 语言中当一个变量被声明的时候,系统会自动初始化它的默认值,比如 int 被初始化为 0,指针为 nil。

var 声明同样也会为结构体类型的数据分配内存,所以我们才能像上一段代码中那样,在声明了 var s T 之后就能直接给他的字段进行赋值

方式二:使用 new

使用 new 函数给一个新的结构体变量分配内存,它返回指向已分配内存的指针:var t *T = new(T)。

type struct1 struct {
    i1 int
    f1 float32
    str string
}

func main() {
    ms := new(struct1)
    ms.i1 = 10
    ms.f1 = 15.5
    ms.str= "Chris"

    fmt.Printf("The int is: %d\n", ms.i1)
    fmt.Printf("The float is: %f\n", ms.f1)
    fmt.Printf("The string is: %s\n", ms.str)
    fmt.Println(ms)
}

与面向对象语言相同,使用点操作符可以给字段赋值: structname.fieldname = value

同样的,使用点操作符可以获取结构体字段的值: structname.fieldname

方式三:使用字面量

type Person struct {
    name string
    age int
    address string
}

func main() {
    var p1 Person
    p1 = Person{"lisi", 30, "shanghai"}   //方式A
    p2 := Person{address:"beijing", age:25, name:"wangwu"} //方式B
    p3 := Person{address:"NewYork"} //方式C
}

方式A中,值必须以字段在结构体定义时的顺序给出。

方式B是在值前面加上了字段名和冒号,这种方式下值的顺序不必一致,并且某些字段还可以被忽略掉,就像 方式C那样。

除了上面这三种方式外,还有一种初始化结构体实体更简短和常用的方式,如下:

ms := &Person{"name", 20, "bj"}
ms2 := &Person{name:"zhangsan"}

&Person{a, b, c} 是一种简写,底层仍会调用 new(),这里值的顺序必须按照字段顺序来写,同样它也可以使用在值前面加上字段名和冒号的写法(见上文的方式B,C)。

表达式 new(Type) 和 &Type{} 是等价的。

到目前为止,我们已经了解了三种初始化结构体的方式:

//第一种,在Go语言中,可以直接以 var 的方式声明结构体即可完成实例化(var 声明同样也会为结构体类型的数据分配内存 )
var t T
t.a = 1
t.b = 2

//第二种,使用 new() 实例化
t := new(T)

//第三种,使用字面量初始化
t := T{a, b} //分配内存,并初始化
t := &T{} //简写方式,等效于 new(T),其实就是上面那种写法加上一个取地址,这样 t 就是指针类型了

使用 var t T 会给 t 分配内存,并零值化内存,但是这个时候的 t 的类型是 T;使用 new 关键字时 t := new(T),变量 t 则是一个指向 T 的指针。

从内存布局上来看,我们就能看出这三种初始化方式的区别:

使用 var 声明:直接为该变量分配内存。

img

使用 new 初始化:为类型分配内存后,返回该类型的指针。

img

使用结构体字面量初始化:相当于上面两种方式的简写。

img

下面来看一个具体的例子

package main
import "fmt"

type Person struct {
 name string
 age int
}

func main() {
 var p1 Person // 分配内存,并初始化为 0 值
 p1.name = "zhangsan"
 p1.age = 18
 fmt.Printf("This is %s, %d years old\n", p1.name, p1.age)

 p2 := new(Person) //分配内存,返回指针
 p2.name = "lisi"
 p2.age = 20
 (*p2).age = 23 //这种写法也是合法的
 fmt.Printf("This is %s, %d years old\n", p2.name, p2.age)

 p3 := Person{"wangwu", 25} //分配内存,并初始化为字面值
 fmt.Printf("This is %s, %d years old\n", p3.name, p3.age)
}

输出:

This is zhangsan, 18 years old
This is lisi, 23 years old
This is wangwu, 25 years old

上面例子的第二种情况,虽然 p2 是指针类型,但我们仍然可以像 p2.age = 23 这样赋值,不需要像 C++ 中那样使用 -> 操作符,Go 会自动进行转换。

注意也可以先通过 * 操作符来获取指针所指向的内容,再进行赋值: (*p2).age = 23

疑点解答

  • 为什么new 出来的指针变量,也可以像 var 声明的变量一样来使用结构体字段?

虽然 p2 是指针类型,但我们仍然可以像 p2.age = 23 这样赋值,不需要像 C++ 中那样使用 -> 操作符,Go 会自动进行转换。

Go 语言中,结构体和它所包含的数据在内存中是以连续块的形式存在的。

即使结构体中嵌套有其他的结构体,这在性能上带来了很大的优势。

不像 Java 中的引用类型,一个对象和它里面包含的对象可能会在不同的内存空间中,这点和 Go 语言中的指针很像。

下面的例子清晰地说明了这些情况:

type Rect1 struct {Min, Max Point }
type Rect2 struct {Min, Max *Point }

img

Original: https://www.cnblogs.com/burner/p/05jie-gou-ti-nei-cun-bu-ju.html
Author: 浪客禅心
Title: go-结构体内存布局

原创文章受到原创版权保护。转载请注明出处:https://www.johngo689.com/606600/

转载文章受原作者版权保护。转载请注明原作者出处!

(0)

大家都在看

  • 安装win10和ubuntu双系统

    2019-06-22 ​ 最近找了一份新的工作,要用到linux,由于之前基本上没有接触过这方面的东西,所以今天捣鼓一下,安装win10和linux双系统,办公研发双不误。如果在安…

    Linux 2023年6月14日
    096
  • 【演讲】2020年马云谈疫情过后的新风口

    2020年马云谈疫情过后的新风口 【关键词】:疫情、新风口、数字化趋势、传统行业转型、教育 一、演讲背景 背景 2020线上智博会,马云8分钟演讲30次提到数字化 原视频 2020…

    Linux 2023年6月13日
    0104
  • Locationhttpd反向代理

    Location\httpd\反向代理 参考:http://tengine.taobao.org/nginx_docs/cn/docs/http/ngx_http_core_mod…

    Linux 2023年6月11日
    089
  • 使用GVT-g为KVM添加虚拟显卡渲染3D图形

    0x00 前言 可以在KVM虚拟机看电影、玩游戏哦。 我的环境如下: 0x01 启动GVT-g 要启用IOMMU、i915,编辑grub文件: $ sudo vim /etc/de…

    Linux 2023年5月27日
    092
  • AI场景存储优化:云知声超算平台基于 JuiceFS 的存储实践

    云知声是一家专注于语音及语言处理的技术公司。 Atlas 超级计算平台是云知声的计算底层基础架构,为云知声在 AI 各个领域(如语音、自然语言处理、视觉等)的模型迭代提供训练加速等…

    Linux 2023年6月14日
    085
  • Linux——基础命令用法(上)

    一、Linux基础命令 1、Linux命令的语法 一条完整的Linux命令的组成部分: 命令 选项 参数 命令:是某个具体的功能 选项:是对函数的修改(通常以-开头,-表示选项的短…

    Linux 2023年5月27日
    072
  • Android:hook很“危险”,使用需谨慎。

    前言 上篇文章《Android安卓进阶技术分享之AGP工作原理》和大家分析了 AGP(Android Gradle Plugin) 做了哪些事,了解到 AGP 就是为打包这个过程服…

    Linux 2023年6月13日
    075
  • oracle 怎么查看用户对应的表空间

    oracle 怎么查看用户对应的表空间? 查询用户: 查看数据库里面所有用户,前提是你是有 dba 权限的帐号,如 sys,system: select * from dba_us…

    Linux 2023年6月6日
    097
  • 匿名远程启动jenkins的job

    安装jenkins插件Build Authorization Token Root job配置中的构建触发器,勾选触发远程构建,输入要用的令牌,如soul 通过jenkins地址调…

    Linux 2023年6月6日
    0117
  • Xbox无法进入开发者模式

    从2020.09.01起,Xbox的dev mode app估计是证书过期或者其他系统配置问题,导致大量开发者无法进入开发者模式,具体如下图所示。 针对上述问题,可以通过微软预先设…

    Linux 2023年6月13日
    0236
  • Linux指令_曾佳豪

    一、基础指令 1、ls指令 含义:ls (list) 用法1 :#ls 含义:列出当前工作目录下所有文件/文件夹的名称 [En] Meaning: list the names o…

    Linux 2023年5月27日
    0114
  • Netty源码解读(三)-NioEventLoop

    先看看EventLoop类图 我们在Netty第二篇文章中的代码中,看到有多次用到eventLoop.execute()方法,这个方法就是EventLoop开启线程执行任务的关键,…

    Linux 2023年6月7日
    087
  • VMware服务关闭后一定要重启

    重要的事情说三遍:服务暂时关闭记得重启,服务暂时关闭记得重启,服务暂时关闭记得重启!!! VMware服务由于安装补丁的需要我暂时把服务关闭了,于是我遇到了尴尬的一幕,于是乎发现上…

    Linux 2023年6月7日
    0111
  • 抓到 Netty 一个隐藏很深的内存泄露 Bug | 详解 Recycler 对象池的精妙设计与实现

    欢迎关注公众号:bin的技术小屋,如果大家在看文章的时候发现图片加载不了,可以到公众号查看原文 本系列Netty源码解析文章基于 4.1.56.Final版本 最近在 Review…

    Linux 2023年6月6日
    0102
  • 内存分配-slab分配器

    1 slab综述 1.1 slab分配器产生的背景 类似 task_struct mm_struct 等结构被内核中被频繁分配和释放,同时创建和销毁这些结构会产生一定的开销(ove…

    Linux 2023年6月7日
    071
  • SpringBoot学习笔记——Redis Template

    Springboot可以通过redis template和redis进行交互,使用方法如下 可以参考这个系列的文章: 【快学springboot】11.整合redis实现sessi…

    Linux 2023年5月28日
    094
亲爱的 Coder【最近整理,可免费获取】👉 最新必读书单  | 👏 面试题下载  | 🌎 免费的AI知识星球