【C语言】字节对齐(内存对齐)
1. 对齐原则:
【原则1】数据成员对齐规则:结构(struct)(或联合(union))的数据成员,第一个数据成员放在offset为0的地方,以后每个数据成员的对齐按照#pragma pack指定的数值和这个数据成员自身长度中,比较小的那个进行。
【原则2】结构(或联合)的整体对齐规则:在数据成员完成各自对齐之后,结构(或联合)本身也要进行对齐,对齐将按照#pragma pack指定的数值和结构(或联合)最大数据成员长度中,比较小的那个进行。
【原则3】结构体作为成员:如果一个结构里有某些结构体成员,则结构体成员要从其内部最大元素大小的整数倍地址开始存储。
备注:数组成员按长度按数组类型长度计算,如char t[9],在第1步中数据自身长度按1算,累加结构体时长度为9;第2步中,找最大数据长度时,如果结构体T有复杂类型成员A,该A成员的长度为该复杂类型成员A的最大成员长度。
小结:当#pragma pack的n值等于或超过所有数据成员长度的时候,这个n值的大小将不产生任何效果。
【注意】(对齐位数跟处理器位数和编译器都有关)VS, VC等编译器默认是#pragma pack(8),所以测试我们的规则会正常;注意gcc默认是#pragma pack(4),并且gcc只支持1,2,4对齐。套用三原则里计算的对齐值是不能大于#pragma pack指定的n值。
2. 自然对齐:存放变量的地址要是该变量数据类型大小的整数倍。如:存放int型数据的地址一定要是4的倍数,存放short型数据的地址一定要是2的倍数。
3. 改变缺省的对界条件(指定对界):
- 使用伪指令#pragma pack (n),C编译器将按照n个字节对齐。
- 使用伪指令#pragma pack(),取消自定义字节对齐方式。
举例一
例1:
1 2 3 4 5 6 7 8 9 10 |
#pragma pack(1)
struct AA
{
???? int a;??? //长度4 < 1 按1对齐;偏移量为0;存放位置区间[0,3]
???? char b;?? //长度1 = 1 按1对齐;偏移量为4;存放位置区间[4]
???? short c;? //长度2 > 1 按1对齐;偏移量为5;存放位置区间[5,6]
???? char d;?? //长度1 = 1 按1对齐;偏移量为6;存放位置区间[7]
???? //整体存放在[0~7]位置区间中,共八个字节。
};
#pragma pack()
|
结果:8个字节
例2:
1 2 3 4 5 6 7 8 9 |
#pragma pack(2)
struct AA
{
???? int a;??? //长度4 > 2 按2对齐;偏移量为0;存放位置区间[0,3]
???? char b;?? //长度1 < 2 按1对齐;偏移量为4;存放位置区间[4]
???? short c;? //长度2 = 2 按2对齐;偏移量要提升到2的倍数6;存放位置区间[6,7]
???? char d;?? //长度1 < 2 按1对齐;偏移量为7;存放位置区间[8];共九个字节
};
#pragma pack()
|
结果:10个字节
例3:
1 2 3 4 5 6 7 8 9 |
#pragma pack(4)
struct AA
{
???? int a;??? //长度4 = 4 按4对齐;偏移量为0;存放位置区间[0,3]
???? char b;?? //长度1 < 4 按1对齐;偏移量为4;存放位置区间[4]
???? short c;? //长度2 < 4 按2对齐;偏移量要提升到2的倍数6;存放位置区间[6,7]
???? char d;?? //长度1 < 4 按1对齐;偏移量为7;存放位置区间[8];总大小为9
};
#pragma pack()
|
结果:12个字节
例4:
1 2 3 4 5 6 7 8 9 |
#pragma pack(8)
struct AA
{
???? int a;??? //长度4 < 8 按4对齐;偏移量为0;存放位置区间[0,3]
???? char b;?? //长度1 < 8 按1对齐;偏移量为4;存放位置区间[4]
???? short c;? //长度2 < 8 按2对齐;偏移量要提升到2的倍数6;存放位置区间[6,7]
???? char d;?? //长度1 < 8 按1对齐;偏移量为7;存放位置区间[8],总大小为9
};
#pragma pack()
|
结果:12个字节
例5:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
struct EE?? //8个字节对齐
{
???? int a;?????? //长度4 < 8 按4对齐;偏移量为0;存放位置区间[0,3]
???? char b;????? //长度1 < 8 按1对齐;偏移量为4;存放位置区间[4]
???? short c;???? //长度2 < 8 按2对齐;偏移量由5提升到6;存放位置区间[6,7]
???? struct FF??? //结构体内部最大元素为int,由于偏移量为8刚好是4的整数倍,所以从8开始存放接下来的struct FF
???? {
???????? int a1;????? //长度4 < 8 按4对齐;偏移量为8;存放位置区间[8,11]
???????? char b1;???? //长度1 < 8 按1对齐;偏移量为12;存放位置区间[12]
???????? short c1;??? //长度2 < 8 按2对齐;偏移量为13,提升到2的倍数14;存放位置区间[14,15]
???????? char d1;???? //长度1 < 8 按1对齐;偏移量为16;存放位置区间[16]
???? };?????????????? //整体对齐系数 = min((max(int,short,char), 8) = 4,将内存大小由17补齐到4的整数倍20
????
???? char d;????????? //长度1 < 8 按1对齐;偏移量为21;存放位置区间[21]
???????????????????? //整体对齐系数 = min((max(int,short,char), 8) = 4,将内存大小由21补齐到4的整数倍24
};
|
1 2 3 4 5 6 7 8 9 10 11 12 13 |
struct B
{
???? char e[2];?????? //长度1 < 8 按2对齐;偏移量为0;存放位置区间[0,1]
???? short h;???????? //长度2 < 8 按2对齐;偏移量为2;存放位置区间[2,3]
???????????????????? //结构体内部最大元素为double,偏移量为4,提升到8,所以从8开始存放接下来的struct A
???? struct A
???? {
???????? int a;?????? //长度4 < 8 按4对齐;偏移量为8;存放位置区间[8,11]
???????? double b;??? //长度8 = 8 按8对齐;偏移量为12,提升到16;存放位置区间16,23]
???????? float c;???? //长度4 < 8,按4对齐;偏移量为24,存放位置区间[24,27]
???? };
???? //整体对齐系数 = min((max(int,double,float), 8) = 8,将内存大小由28补齐到8的整数倍32
};
|
举例二
代码1:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 |
#include <stdio.h>
typedef struct
{
???? int aa1;??? //4个字节对齐 1111?
???? char bb1;?? //1个字节对齐 1?
???? short cc1;? //2个字节对齐 011?
???? char dd1;?? //1个字节对齐 1?
}testlength1;
int length1 =? sizeof (testlength1);? //4个字节对齐,占用字节1111 1011 1000,length = 12
typedef struct
{
???? char bb2;?? //1个字节对齐 1?
???? int aa2;??? //4个字节对齐 01111?
???? short cc2;? //2个字节对齐 11?
???? char dd2;?? //1个字节对齐 1?
}
testlength2;
int length2 =? sizeof (testlength2);? //4个字节对齐,占用字节1011? 1111 1000,length = 12
typedef struct
{
???? char bb3;??? //1个字节对齐 1?
???? char dd3;??? //1个字节对齐 1?
???? int aa3;???? //4个字节对齐 001111?
???? short cc23;? //2个字节对齐 11?
}testlength3;
int length3 =? sizeof (testlength3);? //4个字节对齐,占用字节1100 1111 1100,length = 12
typedef struct
{
???? char bb4;?? //1个字节对齐 1?
???? char dd4;?? //1个字节对齐 1?
???? short cc4;? //2个字节对齐 11?
???? int aa4;??? //4个字节对齐 1111?
} testlength4;
int length4 =? sizeof (testlength4);? //4个字节对齐,占用字节1111 1111,length = 8
int main( void )
{
???? printf ( "length1 = %d.\n" ,length1);
???? printf ( "length2 = %d.\n" , length2);
???? printf ( "length3 = %d.\n" , length3);
???? printf ( "length4 = %d.\n" , length4);
???? return 0;
}
|
VS2017输出结果:
代码2:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 |
#include <stdio.h>
#pragma pack(2)
typedef? struct
{
???? int aa1;??? //2个字节对齐 1111?
???? char bb1;?? //1个字节对齐 1?
???? short cc1;? //2个字节对齐 011?
???? char dd1;?? //1个字节对齐 1?
} testlength1;
int length1 =? sizeof (testlength1);? //2个字节对齐,占用字节11 11 10 11 10,length = 10
typedef? struct
{
???? char bb2;???? //1个字节对齐 1
???? int aa2;????? //2个字节对齐 01111?
???? short cc2;??? //2个字节对齐 11?
???? char dd2;???? //1个字节对齐 1?
} testlength2;
int length2 =? sizeof (testlength2);? //2个字节对齐,占用字节10 11 11 11 10,length = 10
typedef? struct
{
???? char bb3;??? //1个字节对齐 1?
???? char dd3;??? //1个字节对齐 1?
???? int aa3;???? //2个字节对齐 11 11?
???? short cc23;? //2个字节对齐 11?
}testlength3;
int length3 =? sizeof (testlength3);? //2个字节对齐,占用字节11 11 11 11,length = 8
typedef? struct
{
???? char bb4;?? //1个字节对齐 1?
???? char dd4;?? //1个字节对齐 1?
???? short cc4;? //2个字节对齐 11?
???? int aa4;??? //2个字节对齐 11 11
}testlength4;
int length4 =? sizeof (testlength4);? //2个字节对齐,占用字节11 11 11 11,length = 8
int main( void )
{
???? printf( "length1 = %d.\n" , length1);
???? printf( "length2 = %d.\n" , length2);
???? printf( "length3 = %d.\n" , length3);
???? printf( "length4 = %d.\n" , length4);
???? return 0;
}
|
VS2017输出结果:
代码3:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 |
#include<iostream>
using namespace std;
typedef struct bb
{
???? int id;????????????? //[0]....[3]
???? double weight;?????? //[8].....[15] 原则1
???? float height;??????? //[16]..[19],总长要为8的整数倍,补齐[20]...[23] 原则3
}BB;
typedef struct aa
{
???? char name[2];???? //[0],[1]
???? int id;????????? //[4]...[7] 原则1
???? double score;???? //[8]....[15]
???? short grade;????? //[16],[17]
???? BB b;???????????? //[24]......[47] 原则2
}AA;
int main()
{
???? AA a;
???? cout <<? sizeof (a) <<? " " <<? sizeof (BB) << endl;
???? return 0;
}
|
VS2017输出结果: 48 24
代码4:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 |
#include<iostream>
using namespace std;
#pragma pack(2)
typedef struct bb
{
???? int id;????????????
???? double weight;?????
???? float height;??????
}BB;
typedef struct aa
{
???? char name[2];???
???? int id;???????
???? double score;???
???? short grade;???
???? BB b;??????????
}AA;
int main()
{
???? AA a;
???? cout <<? sizeof (a) <<? " " <<? sizeof (BB) << endl;
???? return 0;
}
|
VS2017输出结果:32 16