原创文章,欢迎转载。转载请注明:转载自 祥的博客
原文链接:https://blog.csdn.net/humanking7/article/details/80979517
1.现象
1.1. 测试代码
#include <iostream>
using namespace std;
//默认对齐方式
typedef struct
{
double a;//8个字节
char b;//1个字节
float c;//4个字节
}DataType;
//8字节对齐方式
#pragma pack(push)
#pragma pack(8)
typedef struct
{
double a;//8个字节
char b;//1个字节
float c;//4个字节
}DataType_8;
#pragma pack(pop)
//4字节对齐方式
#pragma pack(push)
#pragma pack(4)
typedef struct
{
double a;//8个字节
char b;//1个字节
float c;//4个字节
}DataType_4;
#pragma pack(pop)
//2字节对齐方式
#pragma pack(push)
#pragma pack(2)
typedef struct
{
double a;//8个字节
char b;//1个字节
float c;//4个字节
}DataType_2;
#pragma pack(pop)
//1字节对齐方式
#pragma pack(push)
#pragma pack(1)
typedef struct
{
double a;
char b;
float c;
}DataType_1;
#pragma pack(pop)
int main()
{
cout << "sizeof( DataType ) = " << sizeof(DataType) << endl;
cout << "sizeof( DataType_1 ) = " << sizeof(DataType_1) << endl;
cout << "sizeof( DataType_2 ) = " << sizeof(DataType_2) << endl;
cout << "sizeof( DataType_4 ) = " << sizeof(DataType_4) << endl;
cout << "sizeof( DataType_8 ) = " << sizeof(DataType_8) << endl;
return 0;
}
1.2. 结果
sizeof( DataType ) = 16
sizeof( DataType_1 ) = 13
sizeof( DataType_2 ) = 14
sizeof( DataType_4 ) = 16
sizeof( DataType_8 ) = 16
1.3. 注意
各家编译器(VS
、gcc
)对与pack
、pop
的解释和支持都是不一样的。
Visual Studio 2013 http://msdn.microsoft.com/en-us/library/2e70t5y1(v=vs.120).aspx
具体可以看看这个博文:http://blog.chinaunix.net/uid-14949191-id-3969367.html
不过在下认为,最主要的还是要多测试,如果换了编译环境,一定要测试先行,血的教训。
2. 相关概念
字节对齐:
现代计算机中,内存空间按照字节划分,理论上可以从任何起始地址访问任意类型的变量。但实际中在访问特定类型变量时经常在特定的内存地址访问,这就需要各种类型数据按照一定的规则在空间上排列,而不是顺序一个接一个地存放,这就是对齐。
结构体对齐:
在C语言中,结构体是种复合数据类型,其构成元素既可以是基本数据类型(如int、long、float等)的变量,也可以是一些复合数据类型(如数组、结构体、联合等)的数据单元。编译器为结构体的每个成员按照其自然边界(alignment)分配空间。各成员按照它们被声明的顺序在内存中顺序存储,第一个成员的地址和整个结构的地址相同。
字节对齐的问题主要就是针对结构体。
四个重要的基本概念:
- 数据类型自身的对齐值:char型数据自身对齐值为1字节,short型数据为2字节,int/float型为4字节,double型为8字节。
- 结构体或类的自身对齐值:其成员中自身对齐值最大的那个值。
- 指定对齐值:#pragma pack (value)时的指定对齐值value。
- 数据成员、结构体和类的有效对齐值:自身对齐值和指定对齐值中较小者,即有效对齐值=min{自身对齐值,当前指定的pack值}。
结构体字节对齐的细节和具体编译器实现相关,但一般而言满足三个准则:
- 结构体变量的首地址能够被其最宽基本类型成员的大小所整除;
- 结构体每个成员相对结构体首地址的偏移量(offset)都是成员大小的整数倍,如有需要编译器会在成员之间加上填充字节(internal adding);
- 结构体的总大小为结构体最宽基本类型成员大小的整数倍,如有需要编译器会在最末一个成员之后加上填充字节{trailing padding}。
3. 扩展概念
位域对齐
有些信息在存储时,并不需要占用一个完整的字节,而只需占几个或一个二进制位。例如在存放一个开关量时,只有0和1两种状态,用一位二进位即可。为了节省存储空间和处理简便,C语言提供了一种数据结构,称为“位域”或“位段”。
位域是一种特殊的结构成员或联合成员(即只能用在结构或联合中),用于指定该成员在内存存储时所占用的位数,从而在机器内更紧凑地表示数据。每个位域有一个域名,允许在程序中按域名操作对应的位。这样就可用一个字节的二进制位域来表示几个不同的对象。
可以继续查看相关博文,在下现在没有对此没有需求,先Mark一下:相关博文