一.内存对齐
许多实际的计算机系统对基本类型数据在内存中存放的位置有限制,它们会要求这些数据的首地址的值是某个数k(通常它为4或8)的倍数,这就是所谓的内存对齐,而这个k则被称为该数据类型的对齐模数(alignment modulus)。当一种类型S的对齐模数与另一种类型T的对齐模数的比值是大于1的整数,我们就称类型S的对齐要求比T强(严格),而称T比S弱(宽松)。
这种强制的要求一来简化了处理器与内存之间传输系统的设计,二来可以提升读取数据的速度。比如这么一种处理器,它每次读写内存的时候都从某个8倍数的地址开始,一次读出或写入8个字节的数据,假如软件能保证double类型的数据都从8倍数地址开始,那么读或写一个double类型数据就只需要一次内存操作。否则,我们就可能需要两次内存操作才能完成这个动作,因为数据或许恰好横跨在两个符合对齐要求的8字节内存块上。某些处理器在数据不满足对齐要求的情况下可能会出错,但是Intel的IA32架构的处理器则不管数据是否对齐都能正确工作。不过Intel奉劝大家,如果想提升性能,那么所有的程序数据都应该尽可能地对齐。
gcc默认情况下采用如下的对齐规则: 任何基本数据类型T的对齐模数就是T的大小,即sizeof(T)。比如对于double类型(8字节),就要求该类型数据的地址总是8的倍数,而char类型数据(1字节)则可以从任何一个地址开始。
举例说明:
struct foo {
char c;
int i;
double d;
};
许多实际的计算机系统对基本类型数据在内存中存放的位置有限制,它们会要求这些数据的首地址的值是某个数k(通常它为4或8)的倍数,这就是所谓的内存对齐,而这个k则被称为该数据类型的对齐模数(alignment modulus)。当一种类型S的对齐模数与另一种类型T的对齐模数的比值是大于1的整数,我们就称类型S的对齐要求比T强(严格),而称T比S弱(宽松)。
这种强制的要求一来简化了处理器与内存之间传输系统的设计,二来可以提升读取数据的速度。比如这么一种处理器,它每次读写内存的时候都从某个8倍数的地址开始,一次读出或写入8个字节的数据,假如软件能保证double类型的数据都从8倍数地址开始,那么读或写一个double类型数据就只需要一次内存操作。否则,我们就可能需要两次内存操作才能完成这个动作,因为数据或许恰好横跨在两个符合对齐要求的8字节内存块上。某些处理器在数据不满足对齐要求的情况下可能会出错,但是Intel的IA32架构的处理器则不管数据是否对齐都能正确工作。不过Intel奉劝大家,如果想提升性能,那么所有的程序数据都应该尽可能地对齐。
gcc默认情况下采用如下的对齐规则: 任何基本数据类型T的对齐模数就是T的大小,即sizeof(T)。比如对于double类型(8字节),就要求该类型数据的地址总是8的倍数,而char类型数据(1字节)则可以从任何一个地址开始。
举例说明:
struct foo {
char c;
int i;
double d;
};
设想一下,sizeof(struct foo)应为多少?
char类型对齐模数为1,所以先不用考虑;然后是int,对齐模数为4,也就是说,它必须从某个4倍数的地址开始,这样,前面的char型至少得加到4个字节(增加3个空的字节),现在,就是8个字节了,然后我们看double,对齐模数为8,好的,前面已经有8个字节,现在增加本身需要的8个字节空间,总共是16个,因此sizeof输出为16!
二.位段
我们知道,一个字节=8位,在所有的简单数据类型中,要数char所占空间最小了吧:只有1字节,但是,某些情况下,我们可能只需要一位就可以表达,例如标记,问题是,我们没有对应的数据类型!
例如:我们有多个作为标记的变量:
unsigned int f1;
unsigned int f2;
unsigned int f3;
unsigned int type;
unsigned int index;
算算看,总共需要多少存储空间:4*5=20字节,太浪费了一点吧!位段的概念由此引出:
struct packed_struct
{
unsigned int f1 :1;
unsigned int f2 :1;
unsigned int f3 :1;
unsigned int type :4;
unsigned int index :7;
};
表示:标志 f1, f2, f3 分别只需要 1 位。变量 type 只需要 4 位, 而变量index只需要7位,总共14位,不超过2个字节。
我们测一下:sizeof(struct packd_struct)为4,奇怪,为什么不是2或3而是4呢?
char类型对齐模数为1,所以先不用考虑;然后是int,对齐模数为4,也就是说,它必须从某个4倍数的地址开始,这样,前面的char型至少得加到4个字节(增加3个空的字节),现在,就是8个字节了,然后我们看double,对齐模数为8,好的,前面已经有8个字节,现在增加本身需要的8个字节空间,总共是16个,因此sizeof输出为16!
二.位段
我们知道,一个字节=8位,在所有的简单数据类型中,要数char所占空间最小了吧:只有1字节,但是,某些情况下,我们可能只需要一位就可以表达,例如标记,问题是,我们没有对应的数据类型!
例如:我们有多个作为标记的变量:
unsigned int f1;
unsigned int f2;
unsigned int f3;
unsigned int type;
unsigned int index;
算算看,总共需要多少存储空间:4*5=20字节,太浪费了一点吧!位段的概念由此引出:
struct packed_struct
{
unsigned int f1 :1;
unsigned int f2 :1;
unsigned int f3 :1;
unsigned int type :4;
unsigned int index :7;
};
表示:标志 f1, f2, f3 分别只需要 1 位。变量 type 只需要 4 位, 而变量index只需要7位,总共14位,不超过2个字节。
我们测一下:sizeof(struct packd_struct)为4,奇怪,为什么不是2或3而是4呢?
其实,哪怕是struct foo {unsigned int f1 :1},也会占用4个字节呢,与unsigned int相同。
总结一下:当位段出现在结构定义中时, 至少会占用等同于unsigned int类型的空间,当所有的位段之和超出,分配另一个unsigned int空间。
总结一下:当位段出现在结构定义中时, 至少会占用等同于unsigned int类型的空间,当所有的位段之和超出,分配另一个unsigned int空间。
另外:unsigned char或者其他类型不必考虑。
三.何时位段会失效
例如:
struct bits
{
unsigned int f1:1;
int word;
unsigned int f3:1;
};
按照内存对齐的规则,位段声明会无效,上述sizeof(struct bits)为12!
例如:
struct bits
{
unsigned int f1:1;
int word;
unsigned int f3:1;
};
按照内存对齐的规则,位段声明会无效,上述sizeof(struct bits)为12!