3、结构体/联合体的布局
struct/union中最大的那个alignment,而这个max(alignment),又取决于编译器的设置(用参数/Zpn或者代码中用 #pragma pack(n)),即自身的对齐要求和编译器设置对齐要求二者中的最小值。
4.1 简单的情况
例如:
#include “stdio.h”
struct SA
{
char g;
double k;
};
int main(int argc, char* argv[])
{
SA a;
printf(”Size=%d\nAddr=%p\n”,sizeof(a),&a);
return 0;
}
输出结果如下:
Size=16
Addr=0012FF70
VC6默认采用8字节对齐,double的对齐要求也是8字节。因此这里输出大小为16,地址按照规则需符合8字节对齐,最后四位为0000。
我们可以加上pack 限制
#include “stdio.h”
#pragma pack(4)
struct SA
{
char g;
double k;
};
int main(int argc, char* argv[])
{
SA a;
printf(”Size=%d\nAddr=%p\n”,sizeof(a),&a);
return 0;
}
则输出为:
Size=12
Addr=0012FF74
将默认的对齐边界设置为4,则成员g仅占用4个字节,因为k在4字节边界上对齐。由于SA的成员最大对齐值为4,所以整个结构体也4字节对齐。所以地址的最后4位可以被4整除。
4.2 嵌套的结构体成员
看一个例子:
#include “stdio.h”
struct SB
{
int c;
double k;
short b;
};
struct SA
{
char g;
SB b;
char k;
};
int main(int argc, char* 1498 argv[])
{
SA a;
printf(”Size=%d\nAddr=%p\n”,sizeof(a),&a);
return 0;
}
输出为:
Size=40
Addr=0012FF58
从这个例子可以看出嵌套结构体的规则其实也很简单。首先找到SA中成员的最大对齐边界,由于b是结构体类型,因此b的对齐边界取决于SB中的成员最大对齐边界:8。
因此,SA中的成员8字节对齐。
由于SB中的成员也是8字节对齐的,所以大小为8*5=40。
而整个变量a的地址也要符合8字节对齐要求。
再看看用了pack(4)的情况:
#include “stdio.h”
#pragma pack(4)
struct SB
{
int c;
double k;
short b;
};
struct SA
{
char g;
SB b;
char k;
};
int main(int argc, char* argv[])
{
SA a;
printf(”Size=%d\nAddr=%p\n”,sizeof(a),&a);
return 0;
}
按照分析,SB的最大对齐边界:4,所以SA的对齐边界也是4,这个结果应该输出:
Size=24
Addr=0012FF68
注意pack(n)的限制,作用域从起始位置开始。例如:
#include “stdio.h”
struct SB
{
int c;
double k;
short b;
};
#pragma pack(4)
struct SA
{
char g;
SB b;
char k;
};
int main(int argc, char* argv[])
{
SA a;
printf(”Size=%d\nAddr=%p\n”,sizeof(a),&a);
return 0;
}
SB按照8字节对齐,SA按照4字节对齐,其成员b整体上满足4字节对齐即可。因此大小是8*3(SB大小)+8(SA的成员g和k的大小):
Size=32
Addr=0012FF60
再看更复杂的例子:
#include “stdio.h”
#pragma pack(4)
struct SB
{
int c;
double k;
short b;
};
#pragma pack(2)
struct SA
{
char g;
SB b;
char k;
};
int main(int argc, char* argv[])
{
SA a;
printf(”Size=%d\nAddr=%p\n”,sizeof(a),&a);
return 0;
}
SB的对齐边界为4,因此大小为16,SA的对齐边界为2(最小值),因此大小为16+4=20:
Size=20
Addr=0012FF6C
再来看看成员变量位置的颠倒会给struct带来什么结果?
#include “stdio.h”
struct SB
{
int c;
short b;
double k;
};
struct SA
{
char g;
SB b;
char k;
};
int main(int argc, char* argv[])
{
SA a;
printf(”Size=%d\nAddr=%p\n”,sizeof(a),&a);
return 0;
}
运行结果如下:
Size=32
Addr=0012FF60
变量a的大小变成了32,因为SB中的double成员k的声明被放在了最后。而int成员c和short成员b各占4字节,就可以保证k处于8字节对齐状态。
因此,SB的大小为16。再加上SA的另外两个成员:g和k分别占用8个字节,所以得到的Size就为32。
同样,地址还是符合8字节对齐边界的要求。
从这个例子也可以看出,通过合理安排结构体成员的声明顺序,可以减少其占用内存的大小。