主体在内存中的表示结构类似于数组,声明后字段按顺序存储在内存空间中。结构在内存中的存储顺序与代码中声明的顺序一致。结构体的布局会自动优化,编译器会严格按照声明的顺序生成代码。在结构体中查找数据时,编译器会完全根据结构体的首地址找到字段的实际地址,然后计算偏移量。字段名称不能用于查找数据。它表示当前字段相对于首地址的偏移量。链表节点结构的机器级表示。在此示例中,结构表示链表的节点。movslq将32位数据扩展为64位数据的符号位扩展。i的值为32位整数,mov只移动4个字节。而rax是一个64位寄存器。为了保证值能被正确复制,必须对其进行符号位扩展。所以我们将i的值扩展为64位,并添加到rax寄存器中。但是为什么目标操作数必须是64位的呢?因为rax后面会参与寻址方式,而地址是64位的,所以参与地址计算的寄存器必须是64位的。因此,我们必须利用rax64位寄存器结构的字节对齐问题。排列紧凑的结构称为非对齐结构。通常不推荐处于未对齐状态的结构。真正的结构体的布局必须遵循字节对齐的原则:对于一个结构体字段,如果一个字段需要k个字节来完成存储,那么这个字段的地址必须是k的整数倍。为什么需要字节对齐?在冯诺依曼结构中,处理器和内存的数据通道由系统总线连接。系统总线包括地址总线、数据总线和控制总线。在字节对齐问题中,主要讨论总线的宽度,尤其是数据总线的宽度。对于64位处理器,其数据总线和地址总线的宽度都是64位。64位数据总线是指如果处理器(CPU)要访问内存,一次要传8个字节(比如把地址100-107的数据全部加载给CPU),CPU无法读取从非8的整数倍的起始地址(如101-108)开始读取8个字节。当数据未对齐时,由于CPU只能从8的整数倍的起始地址加载数据,这意味着CPU需要进行两次内存访问。最后放在一起读取数据。这是因为Intel的X86处理器支持一条访问指令访问8字节的数据,但是访问地址不是8的整数倍;这时CPU会自动加载内存数据两次,并在内部拼凑数据。这种情况在许多其他处理器中是不允许的。即使能够读取此类数据也存在性能问题。由于未对齐,需要两次内存访问才能读取,这将大大降低性能。当一个int变量以2的整数倍而不是4的整数倍作为起始地址存储时,虽然数据可以一次访问读取。但是会造成内存碎片,即左右空格不能存放int变量,造成空间浪费。综上所述,x86处理器推荐使用对齐方式,否则性能会下降;其他处理器必须对齐,否则会出错。字节对齐的工作是由编译器完成的,即编译器在编译时自动插入结构和当前情况的保留字节,使布局满足字节对齐要求。x86系统中字节对齐是如何实现的X8664-bit系统字节对齐规则和上面介绍的完全一样。简单的表示就是1、4、8、16字节数据类型的对齐。要求起始地址二进制形式的最低1、2、3、4位为32位平台的0X861、2、4。byte数据类型的对齐与64位平台一致(值得注意的是32位中long和pointer类型都是4字节)。但是,它对于8字节(双精度)和64位平台是不同的。在Windows等操作系统中,要求放置8的整数倍的起始地址;而linux只需要4的整数倍,为什么32位平台对8字节的数据类型有特殊要求?在X8632位系统中,数据总线只有32位,所以不管是8字节对齐还是4字节对齐,都是访问了两次,对性能没有影响。那么为什么Windows等系统要求8字节对齐呢?这样不仅没有性能提升,反而占用了更多的空间。其实主要是为了兼容以后的64位系统。linux系统主要考虑占用空间,所以要求8字节对齐。值得注意的是,我们上面讨论的是X86处理器默认的字节对齐方式。由于在X86处理器中也可以访问不按规则对齐的数据,所以默认规则不是强制规则,但在这种情况下是最高效的。我们甚至可以在X86编程中使用某些关键字或编译选项来要求新的字节对齐规则。比如最多4字节对齐、2字节对齐、不对齐等。这些强制字节对齐通常用于特殊情况。字节对齐起始地址和结束地址的细节起始地址取决于K的值,k是整个结构中占空间最多的数据类型元素的空间宽度。起始地址必须是k的整数倍。尾字节对齐问题也是一样。结束地址之后的最后一个地址必须是k的整数倍。这主要是考虑到结构体中的所有字段都满足字节对齐要求,不浪费字节对齐空间。传递起始地址和结束地址的要求。结构中各元素紧密相连,起始地址和结束地址满足字节对齐的基本要求。结构体数组访问movzwl指令的机器级实现:w表示操作2字节数据;l表示对四字节数据进行操作,z表示0扩展;这条指令表示从该地址取出2个字节的数据(short),填充到32位寄存器中,高16位补0extension如何在程序设计中更好地利用空间将相同的数据类型放在一起(因为结构体的存储顺序与声明顺序相同,所以声明顺序可能会影响存储空间)Union联合体是C语言中的一种特殊语言,在其他语言中很少见。定义方法与结构体类似,但内存布局与结构体有很大不同。结构体中的字段在内存中是平铺的,每个字段在内存中依次展开;union在存储上是重叠的,每个字段共享相同的存储空间。union的存储空间取决于最大数据类型的宽度。union可以存储double类型的数据,然后通过int类型访问double类型的低4字节和高4字节数据,通常可以起到转换数据类型的作用。但与强制类型转换不同的是,强制类型转换是根据值进行转换,值不变;又因为union字段是内存共享的,所以是位级别转换,二进制值不变,值会变。Union应用场景少,比较少见
