在计算结构体类型变量的大小时,就涉及到了内存对齐问题。
1. 结构体内存对齐规则
对齐数 = 编译器默认的一个对齐数 与 该成员大小的较小值
- vs中的默认值为8;
- Linux中的默认值为4;
-
原则1:数据成员的对齐规则(以最大的类型字节为单位)。
结构体(struct)的数据成员,第一个数据成员放在offset为0的地方,以后每个数据成员存放在offset为该数据成员大小的整数倍的地方(比如int在32位机为4字节,则要从4的整数倍地址开始存储) -
原则2:结构体作为成员的对齐规则。
如果一个结构体B里嵌套另一个结构体A,则结构体A应从offset为A内部最大成员的整数倍的地方开始存储。(struct B里存有struct A,A里有char,int,double等成员,那A应该从8的整数倍开始存储。),结构体A中的成员的对齐规则仍满足原则1、原则2。
**(注意)**结构体A所占的大小为该结构体成员内部最大元素的整数倍,不足补齐;不是直接将结构体A的成员直接移动到结构体B中。 -
收尾工作
结构体的总大小,也就是sizeof的结果,必须是其内部最大成员的整数倍,不足的要补齐。
示例1:
struct
{
int a;
short b;
}A;
//sizeof(A) = 8;
struct
{
int a;
char b;
short c;
}A;
//sizeof(A) = 8;
struct
{
char b;
int a;
short c;
}A;
//sizeof(A) = 12;
struct A
{
int a;
double b;
float c;
};
struct B
{
char e[2];
int f;
double g;
short h;
struct A i;
}B;
//sizeof(B) = 48;
struct A
{
int a;
double b;
float c;
};
struct
{
char e[2];
int f;
Int g;
short h;
struct A i;
}B;
//sizeof(B) = 40
#pragma pack(2) //指定对齐单位为2个字节
typedef struct
{
int a;
char b;
short c;
char d;
}A;
//sizeof(A) = 10;
2. 有关不完整类型的字节对齐(位域)
使用位域的主要目的是压缩存储,其大致规则为:
- 一个位域必须存储在同一个字节中,不能跨两个字节。如一个字节所剩空间不够存放另一位域时,应从下一单元起存放该位域。
- 如果相邻位域字段的类型相同,且其位宽之和小于类型的sizeof大小,则后面的字段将紧邻前一个字段存储,直到不能容纳为止;
- 如果相邻位域字段的类型相同,但其位宽之和大于类型的sizeof大小,则后面的字段将从新的存储单元开始,其偏移量为其类型大小的整数倍;
- 如果相邻的位域字段的类型不同,则各编译器的具体实现有差异,VC6采取不压缩方式,Dev-C++和gcc采取压缩方式;
- 如果位域字段之间穿插着非位域字段,则不进行压缩;
- 整个结构体的总大小为最宽基本类型成员大小的整数倍。
示例2:
//以 4 字节对齐
struct A
{
int a1:5;
int a2:9;
char c;
int b:4;
short s;
}B;
//sizeof(B) = 16
#pragma pack(2) //2字节对齐
struct A
{
int a1 : 5;
int a2 : 9;
char c;
int b : 4;
short s;
}B;
//sizeof(B) = 12;
struct A
{
int a : 5;
int b : 7;
int c : 6;
int d : 9;
char e : 2;
int x;
}B;
//sizeof(B) = 12
3. 为什么存在内存对齐
- 平台原因(移植原因):不是所有硬件平台都能访问任意地址上的数据的;某些硬件平台只能在某些地址处区某些特定类型的数据,否则抛出硬件异常;
- 性能原因:数据结构(尤其是栈)应该尽可能的在自然边界上对齐。原因在于,为了访问未对齐的内存,处理器需要做两次内存访问;而对齐内存只需要一次访问;
本文介绍了C语言中结构体的内存对齐规则,包括数据成员对齐、结构体作为成员的对齐以及位域的内存对齐。结构体的大小必须是其内部最大成员的整数倍,位域的存储则会根据编译器和类型有不同的对齐策略。内存对齐对于平台移植和性能优化具有重要意义。

7359

被折叠的 条评论
为什么被折叠?



