一、计算原则和步骤
(1)首先确定指定对齐值:通常由预编译指令#pragma pack(n)来指定位n,若没有这个语句。
那么默认的指定对齐值就是32位系统位4,64位系统位8。
(2)计算结构体各个成员自己的有效对齐值
为min(自身数据类型长度,指定对齐值)。
(3)得到整个结构体的有效对齐值
取其成员的有效对齐值的最大那个作为整个结构体的有效对齐值。
(4)手画一下,在内存存放过程:每个成员的存放首地址要为其有效对齐值的整数倍,不满足则要补出来。
(5)最后要保证结构体占用内存总大小(sizeof的结果)要为结构体的有效对齐值的整数倍,不够要补。
(6)特殊情况注意:
①当结构体成员有数组时,该数组类型成员的首地址仍然按数组类型来计算其对齐值。但在第(5)步中存放时,要按实际的数组元素个数来放。
②当结构体成员有内嵌结构体时,该内嵌结构体的地址按与它自己的有效对齐值对齐,其成员的地址仍然按前面的规则来。注意实际存放时要也要保证该内嵌结构体的总占内存大小是该内嵌结构体的有效对齐长度的整数倍。不足的话反正都是补齐。
二、例子
例1:
struct
{
int a;
short b;
}A;
解:
指定对齐值 | 8(64位系统) |
结构体成员 | 有效对齐值 |
a | 4 |
b | 2 |
结构体对齐值 | 4 |
内存排布:*表示补填
a | a | a | a |
b | b | * | * |
故:sizeof(A) = 8
例2
struct
{
int a;
char b;
short c;
}A;
解:
指定对齐值 | 8(64位系统) |
结构体成员 | 有效对齐值 |
a | 4 |
b | 1 |
c | 2 |
结构体对齐值 | 4 |
内存排布:*表示补填
a | a | a | a |
b | * | c | c |
故:sizeof(A) = 8
例3
struct
{
char b;
int a;
short c;
}A;
解:
指定对齐值 | 8(64位系统) |
结构体成员 | 有效对齐值 |
b | 1 |
a | 4 |
c | 2 |
结构体对齐值 | 4 |
内存排布:*表示补填
b | * | * | * |
a | a | a | a |
c | c | * | * |
故: sizeof(A) = 12;
例4
struct A
{
int a;
double b;
float c;
};
struct
{
char e[2];
int f;
double g;
short h;
struct A i;
}B;
解:
指定对齐值 | 8(64位系统) | |
成员 | 有效对齐值 | |
结构体B成员 | 数组e[2] | 1 |
f | 4 | |
g | 8 | |
h | 2 | |
struct A i.a | 4 | |
struct A i.b | 8 | |
struct A i.c | 4 | |
struct A i | 8 | |
结构体B对齐值 | 8 |
内存排布:
内存排布 | |||||||
e | e | * | * | f | f | f | f |
g | g | g | g | g | g | g | g |
h | h | * | * | * | * | * | * |
i.a | i.a | i.a | i.a | * | * | * | * |
i.b | i.b | i.b | i.b | i.b | i.b | i.b | i.b |
i.c | i.c | i.c | i.c | * | * | * | * |
故:sizeof(B) = 48;
例5
struct A
{
int a;
double b;
float c;
};
struct
{
char e[2];
int f;
int g;
short h;
struct A i;
}B;
解:
指定对齐值 | 8(64位系统) | |
成员 | 有效对齐值 | |
结构体B成员 | 数组e[2] | 1 |
f | 4 | |
g | 4 | |
h | 2 | |
struct A i.a | 4 | |
struct A i.b | 8 | |
struct A i.c | 4 | |
struct A i | 8 | |
结构体B对齐值 | 8 |
内存排布:
内存排布 | |||||||
e | e | * | * | f | f | f | f |
g | g | g | g | h | h | * | * |
i.a | i.a | i.a | i.a | * | * | * | * |
i.b | i.b | i.b | i.b | i.b | i.b | i.b | i.b |
i.c | i.c | i.c | i.c | * | * | * | * |
故:sizeof(B) = 40
例6
struct A
{
char e;
short f;
};
struct
{
int a;
char b;
struct A c;
char d;
}B;
解:
指定对齐值 | 8(64位系统) | |
成员 | 有效对齐值 | |
结构体B成员 | a | 4 |
b | 1 | |
struct A i.e | 1 | |
struct A i.f | 2 | |
struct A i | 2 | |
d | 1 | |
结构体B对齐值 | 4 |
内存排布:
a | a | a | a |
b | * | i.e | * |
i.f | i.f | d | * |
故:sizeof(B) = 12;
例7
struct A
{
int e;
short f;
};
struct
{
int a;
char b;
struct A c;
short d;
}B;
解:
指定对齐值 | 8(64位系统) | |
成员 | 有效对齐值 | |
结构体B成员 | a | 4 |
b | 1 | |
struct A i.e | 4 | |
struct A i.f | 2 | |
struct A i | 4 | |
d | 2 | |
结构体B对齐值 | 4 |
内存排布:
a | a | a | a |
b | * | * | * |
i.e | i.e | i.e | i.e |
i.f | i.f | * | * |
d | d | * | * |
故:sizeof(B) = 20;
例8
#pragma pack(2) //指定对齐值为2个字节
typedef struct
{
int a;
char b;
short c;
char d;
}A;
解:
指定对齐值 | 2 |
结构体成员 | 有效对齐值 |
a | 2 |
b | 1 |
c | 2 |
d | 1 |
结构体对齐值 | 2 |
内存排布:
a | a | a | a |
b | * | c | c |
d | * |
故:sizeof(A) = 10;
例9
#pragma pack(2)
typedef struct
{
char a;
char b;
short c;
int d;
}A;
解:
指定对齐值 | 2 | |
成员 | 有效对齐值 | |
结构体B成员 | a | 1 |
b | 1 | |
c | 2 | |
d | 2 | |
结构体B对齐值 | 2 |
内存排布
a | b | c | c |
d | d | d | d |
故:sizeof(A) = 8;
例10
#pragma pack(1)
typedef struct
{
int d;
char a;
short c;
char b;
}A;
解:
指定对齐值 | 1 |
结构体成员 | 有效对齐值 |
a | 1 |
b | 1 |
c | 1 |
d | 1 |
结构体对齐值 | 1 |
内存排布:
d | d | d | d |
a | c | c | b |
故:sizeof(A) = 8
三、结构体位域相关的字节对齐
结构体使用位域的主要目的是减小存储空间消耗,其大致规则为:
1)只有int或unsigned xxx型能使用位域。
2) 一个位域必须存储在同一个存储单元(指其数据类型存储单元,如int型,则位4byte)中,不能跨两个存储单元。如一个存储单元所剩空间不够存放另一位域时,应从下一存储单元起存放该位域。
3)无名非0位域,表明这个位段空间不可用。
4)无名0位域,表明下一个位段要从下一存储单元开始。
5) 如果相邻位域字段的类型相同,且其位宽之和小于类型的sizeof大小,则后面的字段将紧邻前一个字段存储,直到不能容纳为止;
6) 如果相邻位域字段的类型相同,但其位宽之和大于类型的sizeof大小,则后面的字段将从新的存储单元开始,其偏移量为其类型大小的整数倍;
7) 整个结构体的总大小为最宽基本类型成员大小的整数倍。
struct A
{
int a:5;
int b:9;
char c;
int d:4;
short e;
}B;
sizeof(B) = 8
#pragma pack(2) //2字节对齐
struct A
{
int a1 : 5;
int a2 : 9;
char c;
int b : 4;
short s;
}B;
sizeof(B) = 8;
struct A
{
int a : 5;
int b : 7;
int c : 6;
int d : 9;
char e : 2;
int f;
}B;
sizeof(B) = 12