Go结构体之内存分配

1.关键词

内存对齐偏移量对齐值

2.内存对齐的规则

内存对齐的规则总体看来分为两部分,这两部分是建立在linux系统的位数的基础之上的。操作系统可以分为32位和64位操作系统,与此相对应,内存的IO是以4字节和8字节为单位进行的,若不采用内存对齐,在读取特定数据时可能导致在总线上进行两次读取。由于上述原因,32位和64位系统下编译器默认的对齐值分别为4字节和8字节。
内存对齐的第一个规则就是在默认设置的对齐值和struct内某一字段的内存占用大小值两个数值之中取最小值作为该字段的对齐值,即对齐值=min(编译器默认值,sizeof(字段类型))
内存对其的第二个规则就是对整个结构体而言也要进行对齐,对齐值取默认值和结构体中字段占用内存最大值两者中的最小值,即对齐值=min(默认值,字段最大内存占用值)

3.示例演示

package main

import (
    "fmt"
    "unsafe"
)

type user1 struct {
    b int8  //int8 => 8bit => 1byte
    i int32 //int32=>32bit => 4byte
    j int64 //int64=>64bit => 8byte
}

type user2 struct {
    b byte
    j int64
    i int32
}

type user3 struct {
    i int32
    b byte
    j int64
}

type user4 struct {
    i int32
    j int64
    b byte
}

type user5 struct {
    j int64
    b byte
    i int32
}

type user6 struct {
    j int64
    i int32
    b byte
}

func main() {
    var u1 user1
    var u2 user2
    var u3 user3
    var u4 user4
    var u5 user5
    var u6 user6

	//unsafe.sizeof()可以求得struct所占用内存大小
    fmt.Println("u1 size is ", unsafe.Sizeof(u1))
    fmt.Println("u2 size is ", unsafe.Sizeof(u2))
    fmt.Println("u3 size is ", unsafe.Sizeof(u3))
    fmt.Println("u4 size is ", unsafe.Sizeof(u4))
    fmt.Println("u5 size is ", unsafe.Sizeof(u5))
    fmt.Println("u6 size is ", unsafe.Sizeof(u6))
}

上述示例的输出结果如下:

u1 size is  16
u2 size is  24
u3 size is  16
u4 size is  24
u5 size is  16
u6 size is  16

同时unsafe包中的Offsetof函数和Alignof函数能够获取结构体中某一字断的偏移量和对齐值,对上述对齐规则进行佐证,用法如下:

//偏移值
unsafe.Offsetof(u1.b)  //偏移量0
unsafe.Offsetof(u1.i)  //偏移量16

//对齐量
unsafe.Alignof(u1.b)

4.示例分析

首先,根据上述第一条规则,可以分别求得结构体每一个字段的对齐量,而偏移值距离已占用内存最近且必须为对齐量的整数倍。此处注意偏移值从0开始,0可以看作任意对齐量的整数倍。
对于struct user1来说:

type user1 struct {
    b int8  //对齐量:min(8, 1)=1,偏移量:0,占据0
    i int32 //对齐量:min(8, 4)=4,偏移量:4(之所以为4是因为比0大且为4的倍数的最小值为4),占据4、5、6、7
    j int64 //对齐量:min(8, 8)=8,偏移量:8(之所以为8是因为比7大且为8的倍数的最小值为8),占据8、9、10、11、12、13、14、15
}
//所以根据第一条规则struct user1内存占用为0-15共16字节

对于struct user2来说:

type user2 struct {
    b byte //对齐量:min(8, 1)=1,偏移量:0,占据0
    j int64 //对齐量:min(8, 8)=8,偏移量:8(之所以为8是因为比0大且为8的倍数的最小值为8),占据8、9、10、11、12、13、14、15
    i int32 //对齐量:min(8, 4)=4,偏移量:16(之所以为16是因为比15大且为4的倍数的最小值为16),占据16、17、18、19
}
//所以根据第一条规则struct user2内存占用为0-19共20个字节

然后,根据第二条规则,对齐量还要针对整个struct进行计算,对于user1和user2而言,其中字段的最大内存占用均为8,所以对齐量均=min(8, 8)=8,偏移值均必须为8的倍数。user1占用内存大小为16,满足此规则,而user2占用内存为20,不符合此规则,必须填充4字节内存至24字节方可满足,所以满足上述两个规则的结构体user1和user2的内存占用分别为16和24,其余struct可依此思路进行分析计算。下面分别表示user1和user2的内存占用情况:

//user1
bxxx|iiii|jjjj|jjjj

//user2
bxxx|xxxx|jjjj|jjjj|iiii|xxxx

5.结论及优化

由此可知,内存对齐规则的存在使得struct中字段的声明顺序对内存占用产生影响,调整字段声明顺序可以优化内存占用,这里将内存占用更大的类型更早声明即可。

  • 6
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
在Go语言中,结构体的初始化可以通过多种方式来实现。一种常见的方式是使用键值对进行初始化。例如,我们可以先定义一个结构体类型,然后使用键值对的方式来分配内存并初始化结构体的成员变量。这样做的好处是可以明确指定每个成员变量的值。另外,键值对初始化是可选的,即可以只初始化部分成员变量,而不用初始化所有成员变量。当不初始化成员变量时,匿名结构体的格式变为:ns := struct { 字段1 字段类型1 字段2 字段类型2 … }。 还有一种方式是使用多个值的列表来初始化结构体。这种方式不需要使用键值对,而是直接通过给出每个成员变量的值来完成初始化。需要注意的是,这种方式需要按照结构体定义的顺序依次给出每个成员变量的值[1]。 总之,对于Go语言结构体的初始化,可以使用键值对初始化或者使用多个值的列表初始化。具体选择哪种方式取决于个人的需求和习惯。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* *2* *3* [Go语言结构体如何进行初始化](https://blog.csdn.net/weixin_52723461/article/details/124819621)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_1"}}] [.reference_item style="max-width: 100%"] [ .reference_list ]

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

uncle_mooncake

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值