码迷,mamicode.com
首页 > 其他好文 > 详细

总结一下内存对齐学习

时间:2020-12-02 12:22:43      阅读:4      评论:0      收藏:0      [点我收藏+]

标签:star   code   一个   机制   64位   工作   视频   c语言   scroll   

什么是内存对齐

以一个例子来说明,以64位系统为例

type test struct {
	a int32
	b byte
}

func main() {
	fmt.Println(unsafe.Sizeof(test{})) // 8
}

理论上int32占4个字节,byte占一个字节,test结构体应该占5个字节才对。但实际上占了8个字节,这就是进行了字节对齐。

为什么会出现字节对齐

现代计算机中内存空间都是按照 byte 划分的,从理论上讲似乎对任何类型的变量的访问可以从任何地址开始,但是实际的RAM内存中存放的位置有限制,它们会要求这些数据的首地址的值是某个数k(通常它为4或8)的倍数,这和RAM的存储原理有关。感兴趣可以去看这个视频。

尽管内存是以字节为单位,但是大部分处理器并不是按字节块来存取内存的。它一般会以2字节,4字节(32位),8字节(64位),16字节甚至32字节为单位来存取内存,我们将上述这些存取单位称为内存存取粒度.

内存存取粒度又称 对齐粒度(也叫对齐模数) 或称 寄存器宽度 或称 机器字长 或称 总线宽度 。gcc中默认#pragma pack(4),可以通过预编译命令#pragma pack(n),n = 1,2,4,8,16来改变这一系数。

假如没有内存对齐机制,数据可以任意存放,现在一个int变量存放在从地址1开始的连续的4个字节地址中,该处理器去取数据时,要先从0地址开始读取第一个4字节块,剔除不想要的字节(0地址),然后从地址4开始读取下一个4字节块,同样剔除不要的数据(5,6,7地址),最后留下的两块数据合并放入寄存器。这会多做很多工作。所以就出现了对齐机制。

建议看完上面的视频再来看下面的例子。

对齐规则

go语言中对齐规则和c语言的一样。 下面所有的描述都是对64位来说。

  • 对于简单类型,如果该类型大小超过8个字节,用8个字节对齐;小于8个字节按该类型的大小对齐,即按小的对齐

  • 对于结构体类型,它的默认对齐方式就是它的所有成员使用的对齐参数中最大的一个,这样在成员是复杂类型时,可以最小化长度。

  • 对齐后的长度必须是成员中最大的对齐参数的整数倍,这样在处理数组时可以保证每一项都边界对齐。

  • 对于数组,比如char a[1024];它的对齐方式和分别写1024个char 是一样的。虽然他的占用大小超过了8,但他的对齐值仍然是1。

对于32位机器来说,机器字长是4;对于128位或更高的机器来说,可能是16,32,64...。

几个例子

  1. 顺序不同,最后的占用大小不同
type User struct {
	a uint8
	b uint64
	c uint16
	d uint32
}

type User2 struct {
	a uint8
	c uint16
	d uint32
	b uint64
}

func main() {
	fmt.Println(unsafe.Sizeof(User{})) // 24
	fmt.Println(unsafe.Sizeof(User2{})) // 16
}
  1. 结构体中包含结构体 的时候的对齐案例
type User struct {
	a uint8  //按1对齐 0%1=0 从0开始到1 占1个字节
	b uint32 //按4对齐 4%4=0 从4开始到8 占4个字节
	c uint16 //按2对齐 8%2=0 从8开始到10 占2个字节
  //User 按最大的元素大小对齐,即uint32=4
	//User到10,10不是4的倍数,往后扩充,所以User占12,对齐值是4
}

type User2 struct {
  a uint8  // 按1对齐 0%1=0 0-1  占1  注意是:[0, 1)下同
  c uint16 // 按2对齐 2%2=0  2-4  占2    [2, 4)
  
	//User:占12个字节,对齐值为4
	u User // 按4对齐 4%4=0 4-16  占12
	
	d uint32 // 按4对齐 16%4==0  16-20 占4
	b uint64 // 按8对齐 24%4==0  24-32 占8
  //User2 按最大的元素大小对齐,即uint64=8
	//User2到32  32是8的倍数,所以User2占32,对齐值是8
}

func main() {
	fmt.Println(unsafe.Sizeof(User{}))  // 12
	fmt.Println(unsafe.Sizeof(User2{})) // 32
}
  1. 结构体中包含数组
type two struct {
	s [100]byte // 按byte=1对齐,
	//two按最大的元素大小 byte=1 对齐,
	//two到100  100是1的倍数,所以two占100,对齐值是1
}

type User2 struct {
	a uint8  // 按1对齐 0%1=0  0-1  占1
	c uint16 // 按2对齐 2%2=0  2-4  占2
  
	//two:占100个字节,但是按元素中最大的对齐(按1对齐),
	s two // 按1对齐 4%1=0   4-104  占100

	d uint32 // 按4对齐 104%4=0  104-108 占4
	b uint64 // 按8对齐 108不行,往后找 112%4=0  112-120 占8
	//User2按最大的元素大小 8 对齐,
	//User2到120  120正好是8的倍数,所以User2占120
}

func main() {
	fmt.Println(unsafe.Sizeof(two{}))   // 100
	fmt.Println(unsafe.Sizeof(User2{})) // 120
}
  1. 结构体包含的结构体中包含数组和其他元素
type two struct {
	s [101]byte // 按byte=1对齐,0-101
	b uint64 // 按8对齐 104%8=0 104-112  占8
	//two按最大的元素大小 8 对齐,
	//two到112  112是8的倍数,所以two占112,对齐值为8
}

type User2 struct {
	a uint8  // 按1对齐 0%1=0  0-1  占1
	c uint16 // 按2对齐 2%2=0  2-4  占2

	//two:占112个字节,但是按元素中最大的对齐(按8对齐),
	s two // 按8对齐 8%8=0   8-120  占112

	d uint32 // 按4对齐 120%4=0  120-124 占4
	b uint64 // 按8对齐 128%8=0  128-136 占8
	//User2按最大的元素大小 8 对齐,
	//User2到136  136正好是8的倍数,所以User2占136
}

func main() {
	fmt.Println(unsafe.Sizeof(two{}))  // 112
	fmt.Println(unsafe.Sizeof(User2{}))// 136
}

推荐阅读

总结一下内存对齐学习

标签:star   code   一个   机制   64位   工作   视频   c语言   scroll   

原文地址:https://www.cnblogs.com/iQXQZX/p/14049734.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!