先看一个结构体
// 写法一 type T1 struct { a int8 b int64 c int16 } // 写法二 type T2 struct { a int8 c int16 b int64 }
对于这两个结构体,都有a、b、c三个定义完全一样的字段,只是在定义结构体的时候字段顺序不一样而已,那么两种写法有什么影响吗?
对于新手来说,感觉着没有什么区别的,只是一个书写顺序不同而已,但对于go编译器来说,则有着很大的区别,特别是在不同架构上(32位/64位)的编译器,在一定程度上对内存的使用大小和执行效率有着一定的不同。这里的主要知识点就是golang语言中的内存对齐概念(alignment guarantee),https://gfw.go101.org/article/memory-layout.html
类型的尺寸和结构体字节填充(structure padding)
Go白皮书只对以下种类的类型的尺寸进行了明确规定。
类型种类 尺寸(字节数) ------ ------ byte, uint8, int8 1 uint16, int16 2 uint32, int32, float32 4 uint64, int64 8 float64, complex64 8 complex128 16 uint, int 取决于编译器实现。通常在 32位架构上为4,在64位 架构上为8。 uintptr 取决于编译器实现。但必须 能够存下任一个内存地址。
Go白皮书没有对其它种类的类型的尺寸最初明确规定。 请阅读值复制成本一文来获取标准编译器使用的各种其它类型的尺寸。
标准编译器(和gccgo编译器)将确保一个类型的尺寸为此类型的对齐保证的倍数。
为了满足上一节中规定的地址对齐保证要求,Go编译器可能会在结构体的相邻字段之间填充一些字节。 这使得一个结构体类型的尺寸并非等于它的各个字段类型尺寸的简单相加之和。
下面是一个展示了一些字节是如何填充到一个结构体中的例子。 首先,从上面的描述中,我们已得知(对于标准编译器来说):
int8int16int64T1T2int64T1T2
type T1 struct { a int8 // 在64位架构上,为了让下一个字段b的地址为8字节对齐, // 需在在字段a这里填充7个字节。在32位架构上,为了让 // 字段b的地址为4字节对齐,需在这里填充3个字节。 b int64 c int16 // 为了让类型T1的尺寸为T1的对齐保证的倍数, // 在64位架构上需在这里填充6个字节,在32架构 // 上需在这里填充2个字节。 } // 类型T1的尺寸在64位架构上位24个字节(1+7+8+2+6), // 在32位架构上为16个字节(1+3+8+2+2)。 // 以保存每个字段都是8(64位架构)或者4(32位架构)的的整数倍 type T2 struct { a int8 // 为了让下一个字段c的地址为2字节对齐, // 需在字段a这里填充1个字节。 c int16 // 在64位架构上,为了让下一个字段b的地址为8字节对齐, // 需在字段c这里填充4个字节。在32位架构上,不需填充 // 字节即可保证字段b的地址为4字节对齐的。 b int64 } // 类型T2的尺寸在64位架构上位16个字节(1+1+2+4+8), // 在32位架构上为12个字节(1+1+2+8)。
T1T2


一个有趣的事实是有时候一个结构体类型中零尺寸类型的字段可能会影响到此结构体类型的尺寸。 请阅读此问答获取详情。
这里推荐一个网站:http://golang-sizeof.tips/,可以用来查看结构的内存布局,Type size的值越小越好,这样就可以对结构体进行一些内存大小优化了。
如果还有些模糊的话可以看一下这篇文章:https://blog.csdn.net/Lazyboy_/article/details/88579966