C++中字节对齐有哪些规则,在设计数据结构中有哪些技巧,以实现内存的最少使用。
说白了就是在定义的时候尽量小的在前面..
微软C编译器(cl.exe for 80×86)的对齐策略:第一: 结构体变量的首地址能够被其最宽基本类型成员的大小所整除;备注:编译器在给结构体开辟空间时,首先找到结构体中最宽的基本数据类型,然后寻找内存地址能被该基本数据类型所整除的位置,作为结构体的首地址。将这个最宽的基本数据类型的大小作为上面介绍的对齐模数。第二: 结构体每个成员相对于结构体首地址的偏移量(offset)都是成员大小的整数倍,如有需要编译器会在成员之间加上填充字节(internal adding);备注:为结构体的一个成员开辟空间之前,编译器首先检查预开辟空间的首地址相对于结构体首地址的偏移是否是本成员的整数倍,若是,则存放本成员,反之,则在本成员和上一个成员之间填充一定的字节,以达到整数倍的要求,也就是将预开辟空间的首地址后移几个字节。第三: 结构体的总大小为结构体最宽基本类型成员大小的整数倍,如有需要,编译器会在最末一个成员之后加上填充字节(trailing padding)。备注:结构体总大小是包括填充字节,最后一个成员满足上面两条以外,还必须满足第三条,否则就必须在最后填充几个字节以达到本条要求。填充字节就是为了使结构体字段满足内存对齐要求而额外分配给结构体的空间。对于结构体本身也存在着对齐要求,ANSI C标准规定结构体类型的对齐要求不能比它所有字段中要求最严格的那个宽松,但是可以更严格(但此非强制要求,VC7.1就仅仅是让它们一样严格)。C标准保证,任何类型(包括自定义结构类型)的数组所占空间的大小一定等于一个单独的该类型数据的大小乘以数组元素的个数。换句话说,数组各元素之间不会有空隙。
总结规则如下:0: 结构体变量的首地址能够被其最宽基本类型成员的大小所整除1: VC6和VC71默认的内存对齐方式是 #pragam pack(8)2: 结构体中每个成员按其类型的对齐参数(通常是这个类型的大小)和指定对齐参数中较小的一个对齐.3: 结构体每个成员相对于结构体首地址的偏移量都是成员大小的整数倍.4: 结构体本身也存在着对齐要求规则,不能比它所有字段中要求最严格的那个宽松.5: 结构体的总大小为结构体最宽基本类型成员大小的整数倍,且应尽量节省内存。6: 在GCC中,对齐模数的准则是:对齐模数最大只能是 4,也就是说,即使结构体中有double类型,对齐模数还是4,所以对齐模数只能是1,2,4。
而且在上述的规则中,第3条里,offset必须是成员大小的整数倍:(1): 如果这个成员大小小于等于4则按照上述准则是可行的,(2): 如果成员的大小大于4,则结构体每个成员相对于结构体首地址的偏移量只能按照是4的整数倍来进行判断是否添加填充。
位域情况:C99规定int、unsigned int和bool可以作为位域类型。但编译器几乎都对此作了扩展,允许其它类型类型的存在。如果结构体中含有位域(bit-field),总结规则如下1) 如果相邻位域字段的类型相同,且其位宽之和小于类型的sizeof大小,则后面的字段将紧邻前一个字段存储,直到不能容纳为止;2) 如果相邻位域字段的类型相同,但其位宽之和大于类型的sizeof大小,则后面的字段将从新的存储单元开始,其偏移量为其类型大小的整数倍;3) 如果相邻的位域字段的类型不同,则各编译器的具体实现有差异,VC6采取不压缩方式(不同位域字段存放在不同的位域类型字节中),Dev-C++和GCC都采取压缩方式;4)如果位域字段之间穿插着非位域字段,则不进行压缩;5) 结构体的总大小为结构体最宽基本类型成员大小的整数倍,且应尽量节省内存。备注:当两字段类型不一样的时候,对于不压缩方式,例如:
struct N { char c:2; int i:4; };
依然要满足不含位域结构体内存对齐准则第3条,i成员相对于结构体首地址的偏移应该是4的整数倍,所以c成员后要填充3个字节,然后再开辟4个字节的空间作为int型,其中4位用来存放i,所以上面结构体在VC中所占空间为8个字节;而对于采用压缩方式的编译器来说,遵循不含位域结构体内存对齐准则第3条,不同的是,如果填充的3个字节能容纳后面成员的位,则压缩到填充字节中,不能容纳,则要单独开辟空间,所以上面结构体N在GCC所占空间应该是4个字节。
还有人对这个问题感兴趣,博文:内存对齐分配策略(含位域模式)
一般是按照结构中最大的那个类型来对齐
1)数据类型自身的对齐值:就是上面交代的基本数据类型的自身对齐值。
2)指定对齐值:#pragma pack (value)时的指定对齐值value。
3)结构体或者类的自身对齐值:其成员中自身对齐值最大的那个值。
4)数据成员、结构体和类的有效对齐值:自身对齐值和指定对齐值中较小的那个值。
有了这些值,就可以很方便的来讨论具体数据结构的成员和其自身的对齐方式。 有效对齐值N是最终用来决定数据存放地址方式的值,最重要。有效对齐N,就是表示“对齐在N上”,也就是说该数据的"存放起始地址%N=0".而数据结构中的数据变量都是按定义的先后顺序来排放的。第一个数据变量的起始地址就是数据结构的起始地址。结构体的成员变量要对齐排放,结构体本身也要根据自身的有效对齐值圆整(就是结构体成员变量占用总长度需要是对结构体有效对齐值的整数倍)
struct B {
char b;int a;short c;
};假设B从地址空间0x0000开始排放。该例子中没有定义指定对齐值,该值默认为4。第一个成员变量b的自身对齐值是1,比指定或者默认指定对齐值4小,所以其有效对齐值为1,所以其存放地址0x0000符合0x0000%1=0.第二个成员变量a,其自身对齐值为4,所以有效对齐值也为 4,所以只能存放在起始地址为0x0004到0x0007这四个连续的字节空间中,复核0x0004%4=0,且紧靠第一个变量。第三个变量c,自身对齐值为2,所以有效对齐值也是2,可以存放在0x0008到0x0009这两个字节空间中,符合0x0008%2=0。所以从0x0000到0x0009存放的都是B内容。再看数据结构B的自身对齐值为其变量中最大对齐值(这里是b)所以就是4,所以结构体的有效对齐值也是4。根据结构体圆整的要求, 0x0009到0x0000=10字节,(10+2)%4=0。所以0x0000A到0x000B也为结构体B所占用。故B从0x0000到0x000B 共有12个字节,sizeof(struct B)=12;
a. 关于数据类型自身的对齐值,不同类型会按不同的字节来对齐。类型 对齐值(字节)char 1
short 2
int 4
float 4
double 4
b. 类、结构体的自身对齐字节值。对于结构体类型与类对象的对齐原则:使用成员当中最大的对齐字节来对齐。比如在Struct A中,int a的对齐字节为4,比char,short都大,所以A的对齐字节为4
c. 指定对齐字节值。意思是指使用了宏 #pragma pack(n)来指定的对齐值
d. 类、结构及成员的有效对齐字节值。有效对齐值=min(类/结构体/成员的自身对齐字节值,指定对齐字节值)。 有效对齐值决定了数据的存放方 式,sizeof 运算符就是根据有效对齐值来计算成员大小的。简单来说, 有效对齐其实就是要求数据成员存放的地址值能被有效对齐值整除,即:地址值%有效对齐值=0
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
每个人心里都住着一个人,或眷念,或暗恋,或想念。
文章 0 评论 0
接受
发布评论
评论(5)
说白了就是在定义的时候尽量小的在前面..
微软C编译器(cl.exe for 80×86)的对齐策略:
第一: 结构体变量的首地址能够被其最宽基本类型成员的大小所整除;
备注:编译器在给结构体开辟空间时,首先找到结构体中最宽的基本数据类型,然后寻找内存地址能被该基本数据类型所整除的位置,作为结构体的首地址。将这个最宽的基本数据类型的大小作为上面介绍的对齐模数。
第二: 结构体每个成员相对于结构体首地址的偏移量(offset)都是成员大小的整数倍,如有需要编译器会在成员之间加上填充字节(internal adding);
备注:为结构体的一个成员开辟空间之前,编译器首先检查预开辟空间的首地址相对于结构体首地址的偏移是否是本成员的整数倍,若是,则存放本成员,反之,则在本成员和上一个成员之间填充一定的字节,以达到整数倍的要求,也就是将预开辟空间的首地址后移几个字节。
第三: 结构体的总大小为结构体最宽基本类型成员大小的整数倍,如有需要,编译器会在最末一个成员之后加上填充字节(trailing padding)。
备注:结构体总大小是包括填充字节,最后一个成员满足上面两条以外,还必须满足第三条,否则就必须在最后填充几个字节以达到本条要求。
填充字节就是为了使结构体字段满足内存对齐要求而额外分配给结构体的空间。对于结构体本身也存在着对齐要求,ANSI C标准规定结构体类型的对齐要求不能比它所有字段中要求最严格的那个宽松,但是可以更严格(但此非强制要求,VC7.1就仅仅是让它们一样严格)。C标准保证,任何类型(包括自定义结构类型)的数组所占空间的大小一定等于一个单独的该类型数据的大小乘以数组元素的个数。换句话说,数组各元素之间不会有空隙。
总结规则如下:
0: 结构体变量的首地址能够被其最宽基本类型成员的大小所整除
1: VC6和VC71默认的内存对齐方式是 #pragam pack(8)
2: 结构体中每个成员按其类型的对齐参数(通常是这个类型的大小)和指定对齐参数中较小的一个对齐.
3: 结构体每个成员相对于结构体首地址的偏移量都是成员大小的整数倍.
4: 结构体本身也存在着对齐要求规则,不能比它所有字段中要求最严格的那个宽松.
5: 结构体的总大小为结构体最宽基本类型成员大小的整数倍,且应尽量节省内存。
6: 在GCC中,对齐模数的准则是:对齐模数最大只能是 4,也就是说,即使结构体中有double类型,对齐模数还是4,所以对齐模数只能是1,2,4。
而且在上述的规则中,第3条里,offset必须是成员大小的整数倍:
(1): 如果这个成员大小小于等于4则按照上述准则是可行的,
(2): 如果成员的大小大于4,则结构体每个成员相对于结构体首地址的偏移量只能按照是4的整数倍来进行判断是否添加填充。
位域情况:
C99规定int、unsigned int和bool可以作为位域类型。但编译器几乎都对此作了扩展,允许其它类型类型的存在。
如果结构体中含有位域(bit-field),总结规则如下
1) 如果相邻位域字段的类型相同,且其位宽之和小于类型的sizeof大小,则后面的字段将紧邻前一个字段存储,直到不能容纳为止;
2) 如果相邻位域字段的类型相同,但其位宽之和大于类型的sizeof大小,则后面的字段将从新的存储单元开始,其偏移量为其类型大小的整数倍;
3) 如果相邻的位域字段的类型不同,则各编译器的具体实现有差异,VC6采取不压缩方式(不同位域字段存放在不同的位域类型字节中),Dev-C++和GCC都采取压缩方式;
4)如果位域字段之间穿插着非位域字段,则不进行压缩;
5) 结构体的总大小为结构体最宽基本类型成员大小的整数倍,且应尽量节省内存。
备注:当两字段类型不一样的时候,对于不压缩方式,例如:
依然要满足不含位域结构体内存对齐准则第3条,i成员相对于结构体首地址的偏移应该是4的整数倍,所以c成员后要填充3个字节,然后再开辟4个字节的空间作为int型,其中4位用来存放i,所以上面结构体在VC中所占空间为8个字节;
而对于采用压缩方式的编译器来说,遵循不含位域结构体内存对齐准则第3条,不同的是,如果填充的3个字节能容纳后面成员的位,则压缩到填充字节中,不能容纳,则要单独开辟空间,所以上面结构体N在GCC所占空间应该是4个字节。
还有人对这个问题感兴趣,博文:内存对齐分配策略(含位域模式)
一般是按照结构中最大的那个类型来对齐
1)数据类型自身的对齐值:就是上面交代的基本数据类型的自身对齐值。
2)指定对齐值:#pragma pack (value)时的指定对齐值value。
3)结构体或者类的自身对齐值:其成员中自身对齐值最大的那个值。
4)数据成员、结构体和类的有效对齐值:自身对齐值和指定对齐值中较小的那个值。
有了这些值,就可以很方便的来讨论具体数据结构的成员和其自身的对齐方式。 有效对齐值N是最终用来决定数据存放地址方式的值,最重要。有效对齐N,就是表示“对齐在N上”,也就是说该数据的"存放起始地址%N=0".而数据结构中的数据变量都是按定义的先后顺序来排放的。第一个数据变量的起始地址就是数据结构的起始地址。结构体的成员变量要对齐排放,结构体本身也要根据自身的有效对齐值圆整(就是结构体成员变量占用总长度需要是对结构体有效对齐值的整数倍)
struct B {
char b;
int a;
short c;
};
假设B从地址空间0x0000开始排放。该例子中没有定义指定对齐值,该值默认为4。第一个成员变量b的自身对齐值是1,比指定或者默认指定对齐值4小,所以其有效对齐值为1,所以其存放地址0x0000符合0x0000%1=0.第二个成员变量a,其自身对齐值为4,所以有效对齐值也为 4,所以只能存放在起始地址为0x0004到0x0007这四个连续的字节空间中,复核0x0004%4=0,且紧靠第一个变量。第三个变量c,自身对齐值为2,所以有效对齐值也是2,可以存放在0x0008到0x0009这两个字节空间中,符合0x0008%2=0。所以从0x0000到0x0009存放的都是B内容。再看数据结构B的自身对齐值为其变量中最大对齐值(这里是b)所以就是4,所以结构体的有效对齐值也是4。根据结构体圆整的要求, 0x0009到0x0000=10字节,(10+2)%4=0。所以0x0000A到0x000B也为结构体B所占用。故B从0x0000到0x000B 共有12个字节,sizeof(struct B)=12;
a. 关于数据类型自身的对齐值,不同类型会按不同的字节来对齐。
类型 对齐值(字节)
char 1
short 2
int 4
float 4
double 4
b. 类、结构体的自身对齐字节值。对于结构体类型与类对象的对齐原则:使用成员当中最大的对齐字节来对齐。比如在Struct A中,int a的对齐字节为4,比char,short都大,所以A的对齐字节为4
c. 指定对齐字节值。意思是指使用了宏 #pragma pack(n)来指定的对齐值
d. 类、结构及成员的有效对齐字节值。有效对齐值=min(类/结构体/成员的自身对齐字节值,指定对齐字节值)。 有效对齐值决定了数据的存放方 式,sizeof 运算符就是根据有效对齐值来计算成员大小的。简单来说, 有效对齐其实就是要求数据成员存放的地址值能被有效对齐值整除,即:地址值%有效对齐值=0