许多实际的计算机系统对基本类型数据在内存中存放的位置有限制,它们会要求这些数据的首地址的值是某个数k(通常它为4或8)的倍数,这就是所谓的内存对齐,而这个k则被称为该数据类型的对齐模数(alignment modulus)。当一种类型S的对齐模数与另一种类型T的对齐模数的比值是大于1的整数,我们就称类型S的对齐要求比T强(严格),而称T比S弱(宽松)。这种强制的要求一来简化了处理器与内存之间传输系统的设计,二来可以提升读取数据的速度。比如这么一种处理器,它每次读写内存的时候都从某个8倍数的地址开始,一次读出或写入8个字节的数据,假如软件能保证double类型的数据都从8倍数地址开始,那么读或写一个double类型数据就只需要一次内存操作。否则,我们就可能需要两次内存操作才能完成这个动作,因为数据或许恰好横跨在两个符合对齐要求的8字节内存块上。某些处理器在数据不满足对齐要求的情况下可能会出错,但是Intel的IA32架构的处理器则不管数据是否对齐都能正确工作。不过Intel奉劝大家,如果想提升性能,那么所有的程序数据都应该尽可能地对齐。
规则:
第一,编译器按照成员列表的顺序给每个成员分配内存.
第二,当成员需要满足正确的边界对齐时,成员之间用额外字节填充.
第三,结构体的首地址必须满足结构体中边界要求最为严格的数据类型所要求的地址.
第四,结构体的大小为其最宽基本类型的整数倍.
sizeof操作符能够得出一个结构体的整体长度,包括因边界对齐而额外填充的那些字节.
offsetof(type, member)宏能求得成员在结构体内的偏移,返回size_t.
例题:某C语言的程序段如下:
struct
{
int a;
char b;
short c;
} record;
record.a = 273;
若record变量的首地址为0xC008,则地址0xC008的内容以及record.c的地址分别为:
A. 0x00、0xC00D
B. 0x00、0xC00E
C. 0x11、0xC00D
D. 0x11、0xC00E
首先,小端存储是指:数据的高位存在高地址,数据的低位存在低地址。
比如十六进制数0x123456,则我们知道12是最高位,34其次,56最低。那么存储的时候,如果用的是小端序,内存地址最小的那个用来存储56,地址增大一些存34,再高一些存12.
接着,重点在于对齐,结构体中会根据第一个数据的大小进行对齐。
问题来了,我们是按照最大数据进行对齐还是按照第一个数据进行对齐?
不同机器上的对齐策略不同,一般按照int的4个字节进行对齐。
而这个结构体共7个字节,所以对齐后是8个字节。
那么这个留空的字节位置在哪?
就需要牵涉到变量起始地址的问题。
规定:变量的起始地址必须能够被自身数据类型的大小整除。
对于273,十六进制是0x111,因此第一个低位字节存储0x11,再高一点的字节存储0x01,后面两个字节为空的不管。好了,看存储char b,这个大小是1个字节,存完之后,如果紧接着就存short c,那么c的起始地址是个奇数,不能被2整除。
因此在char b后留白一个字节。
原文链接:https://www.cnblogs.com/steady/archive/2010/11/05/1870296.html
https://www.cnblogs.com/steady/archive/2010/11/05/1870296.html