对于char型数据,其自身对齐值为1,对于short型为2,对于int,float,double类型,其自身对齐值为4,单位字节。 这里面有四个概念值: 1)数据类型自身的对齐值:就是上面交代的基本数据类型的自身对齐值。 2)指定对齐值:#pragma pack (value)时的指定对齐值value。 3)结构体或者类的自身对齐值:其成员中自身对齐值最大的那个值。 4)数据成员、结构体和类的有效对齐值:自身对齐值和指定对齐值中较小的那个值。 有了这些值,我们就可以很方便的来讨论具体数据结构的成员和其自身的对齐方式。有效对齐值N是最终用来决定数据存放地址方式的值,最重要。有效对齐N,就是表示“对齐在N上”,也就是说该数据的"存放起始地址%N=0".而数据结构中的数据变量都是按定义的先后顺序来排放的。第一个数据变量的起始地址就是 数据结构的起始地址。结构体的成员变量要对齐排放,结构体本身也要根据自身的有效对齐值圆整(就是结构体成员变量占用总长度需要是对结构体有效对齐值的整 数倍,结合下面例子理解)。这样就不难理解上面的几个例子的值了。 例子分析: 分析例子B; 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; 同理,分析上面例子C: #pragma pack (2) /*指定按2字节对齐*/ struct C { char b; int a; short c; }; #pragma pack () /*取消指定对齐,恢复缺省对齐*/ 第一个变量b的自身对齐值为1,指定对齐值为2,所以,其有效对齐值为1,假设C从0x0000开始,那么b存放在0x0000,符合0x0000%1= 0;第二个变量,自身对齐值为4,指定对齐值为2,所以有效对齐值为2,所以顺序存放在0x0002、0x0003、0x0004、0x0005四个连续 字节中,符合0x0002%2=0。第三个变量c的自身对齐值为2,所以有效对齐值为2,顺序存放 在0x0006、0x0007中,符合 0x0006%2=0。所以从0x0000到0x00007共八字节存放的是C的变量。又C的自身对齐值为4,所以 C的有效对齐值为2。又8%2=0,C只占用0x0000到0x0007的八个字节。所以sizeof(struct C)=8. 有 了以上的解释,相信你对C语言的字节对齐概念应该有了清楚的认识了吧。在网络程序中,掌握这个概念可是很重要的喔,在不同平台之间(比如在Windows 和Linux之间)传递2进制流(比如结构体),那么在这两个平台间必须要定义相同的对齐方式,不然莫名其妙的出了一些错,可是很难排查的哦^_^。
Intel、微软等公司曾经出过一道类似的面试题: #include <iostream.h> #pragma pack(8) struct example1 { short a; long b; }; struct example2 { char c; example1 struct1; short e; }; #pragma pack() int main(int argc, char* argv[]) { example2 struct2; cout << sizeof(example1) << endl; cout << sizeof(example2) << endl; cout << (unsigned int)(&struct2.struct1) - (unsigned int)(&struct2) << endl; return 0; } 问程序的输入结果是什么? 答案是: 8 16 4 面试题的解答 至此,我们可以对Intel、微软的面试题进行全面的解答。 程序中第2 行#pragma pack (8)虽然指定了对界为8,但是由于struct example1 中的成员最大 size 为4(long 变量size 为4),故struct example1 仍然按4 字节对界,struct example1 的size 为8,即第18 行的输出结果; struct example2 中包含了struct example1,其本身包含的简单数据成员的最大size 为2(short 变量e),但是因为其包含了struct example1,而struct example1 中的最大成员size 为4,struct example2 也应以4 对界,#pragma pack (8)中指定的对界对struct example2 也不起作用,故19 行的 输出结果为16; 由于struct example2 中的成员以4 为单位对界,故其char 变量c 后应补充3 个空,其后才是 成员struct1 的内存空间,20 行的输出结果为4。
union A { int a[5]; char b; double c; }; struct B { int n; A a; char c[10]; }
sizeof(B) = ?
答案:
union A: { int a[5]; //20 char b; //1 double c; //8 }
我想的是union中变量共用内存,应以最长的为准,那就是20。可实际不然,sizeof(A)=24,
A中各变量的默认内存对齐方式,必须以最长的double 8字节对齐,故应该是sizeof(A)=24。
struct B { int n; // 4字节 A a; // 24字节 char c[10]; // 10字节 }; 实际占用38字节,但由于A是8字节对齐的,所以int n和char c[10]也需要8字节对齐,总共8+24+16=48