结构体对齐的具体含义

系统需要存储一些参数,为了方便,把这些参数统一放到一个结构体中:
class Config_Cell
{
public:
    char                MN[14];
    unsigned char        ST[2];   
    unsigned int         SampleInterval;
    unsigned int         ReportInterval;                //4 上报周期=采集周期
    unsigned int         StoreInterval;                //4 历史数据存储周期
    char                CallNum[16];
    char             PassWord[6];
    unsigned int         OverTime;                    //4 超时时间
    unsigned int         ReCount;                    //4 重发次数
    unsigned int         AlarmConfirmTime;            //4 超限报警时间
    unsigned char     IPandPort1[20];                //4 IP & Port- 192.168.001.001.5002
    unsigned char     IPandPort2[20];                //4 IP & Port- 192.168.001.001.5002
    unsigned char     LocalIP[4];                    //4 Local IP
    unsigned char     GateWay[4];                
    unsigned char        FixRepTime[2];                    //4 fixed Report time
    unsigned char        ReportEnabeled:6;                //4前5位 6 -unitControl
    unsigned char     LocalCode:2;                        //地方代码: 旧版本0,国家 1  ,河南2  
};

class saveConfigType
{
public:
    Config_Cell    config;
    unsigned short  CRC;
public:
    bool  CrcCheck();    //4 比较计算Config得到的CRC 和.CRC是否一致
};

系统最终把一个saveConfigType类型存储在NorFlash中,注意还加上了crc校验。

在取出参数时,取出crc检验值,然后再根据Config_Cell    config 计算出一个crc 值,如果这两个值相等,
则认为参数是有效的。
但在实际使用中却出现了问题,该问题是由结构体对齐导致的:
修改了Config_Cell  的定义,删掉了unsigned int SampleInterval 变量。
按照我的想法,crc校验值应该比对不正确,但是实际上,crc校验值比对依然是正确的,
这就导致了取出来的参数成了随机数,系统无法正常工作。

分析其原因是这样的,
虽然类Config_Cell 的实际长度变了,但是由于对齐的原因,sizeof(Config_Cell)的结果却没有发生变化,
sizeof(saveConfigType)的结果也没有发生变化
而CRC所存储在NorFalsh中的位置也没有发生变化,saveConfigType.CRC的位置也没有发生变化,
而crc校验值比对的算法是:取出sizeof(Config_Cell)长度的内容,进行crc计算,算出的值与saveConfigType.CRC
进行比较。这也自然是对的。

关于结构对齐的详细解释是这样的:
//////////////////////////////////////////////////////////////////////////////////////////
以下为转贴的内容:
#pragma pack(4)
TestB class
{
public:
int aa;
char a;
short b;
char c;
};
int nSize = sizeof(TestB);
这里nSize结果为12,在预料之中。

现在去掉第一个成员变量为如下代码:
#pragma pack(4)
class TestC
{
public:
char a;
short b;
char c;
};
int nSize = sizeof(TestC);
按照正常的填充方式nSize的结果应该是8,为什么结果显示nSize为6呢?

事实上,很多人对#pragma pack的理解是错误的。
#pragma pack规定的对齐长度,实际使用的规则是:
结构,联合,或者类的数据成员,第一个放在偏移为0的地方,以后每个数据成员的对齐,按照#pragma pack指定的数值和这个数据成员自身长度中,比较小的那个进行。
也就是说,当#pragma pack的值等于或超过所有数据成员长度的时候,这个值的大小将不产生任何效果。
而结构整体的对齐,则按照结构体中最大的数据成员 和 #pragma pack指定值 之间,较小的那个进行。

具体解释
#pragma pack(4)
class TestB
{
public:
int aa; //第一个成员,放在[0,3]偏移的位置,
char a; //第二个成员,自身长为1,#pragma pack(4),取小值,也就是1,所以这个成员按一字节对齐,放在偏移[4]的位置。
short b; //第三个成员,自身长2,#pragma pack(4),取2,按2字节对齐,所以放在偏移[6,7]的位置。
char c; //第四个,自身长为1,放在[8]的位置。
};
这个类实际占据的内存空间是9字节
类之间的对齐,是按照类内部最大的成员的长度,和#pragma pack规定的值之中较小的一个对齐的。
所以这个例子中,类之间对齐的长度是min(sizeof(int),4),也就是4。
9按照4字节圆整的结果是12,所以sizeof(TestB)是12。


如果
#pragma pack(2)
    class TestB
{
public:
int aa; //第一个成员,放在[0,3]偏移的位置,
char a; //第二个成员,自身长为1,#pragma pack(4),取小值,也就是1,所以这个成员按一字节对齐,放在偏移[4]的位置。
short b; //第三个成员,自身长2,#pragma pack(4),取2,按2字节对齐,所以放在偏移[6,7]的位置。
char c; //第四个,自身长为1,放在[8]的位置。
};
//可以看出,上面的位置完全没有变化,只是类之间改为按2字节对齐,9按2圆整的结果是10。
//所以 sizeof(TestB)是10。

最后看原贴:
现在去掉第一个成员变量为如下代码:
#pragma pack(4)
class TestC
{
public:
char a;//第一个成员,放在[0]偏移的位置,
short b;//第二个成员,自身长2,#pragma pack(4),取2,按2字节对齐,所以放在偏移[2,3]的位置。
char c;//第三个,自身长为1,放在[4]的位置。
};
//整个类的大小是5字节,按照min(sizeof(short),4)字节对齐,也就是2字节对齐,结果是6
//所以sizeof(TestC)是6。




原文地址:https://www.cnblogs.com/gaotaozhaolei/p/1200909.html