#pragma pack(push,1)与#pragma pack(1)的区别

前言:今天看师傅的代码,我还以为是师傅写了个bug,兴致冲冲的改掉。结果细想之下发现我皮皮马就是个瓜皮,幸亏没跟师傅说他写了个bug,不然肯定得被笑死了。

一、事情发生始末

下面就先把涉及到的代码给大家看一下:

1
2
3
4
5
6
7
typedef struct
{
    Uint16 wSerialId;      
    Uint16 wProtocolFlag;
    Uint16 wMsgLen;
    Uint8  byDevAddr;
}TMDBusTcpHead;`

这里定义了一个Modbus协议的消息头的结构体,里面4个数据共7个字节,但是由于编译器字节对齐的原因,结构体占8个字节(原因在其他博客中细谈)。

项目是收到Modbus协议数据_recvBuf进行解析,要将指针偏移到数据的协议头之后,也就是_recvBuf[7]这个位置。下面是我师傅写的地址偏移代码:

1
2
3
4
char* NetIOMBusTcpImpl::getmsgBuf()
{
    return &_recvBuf[sizeof(TMDBusTcpHead)];
}

看到这段代码之后,我瞬间心想:“握草,我读书少不要骗我。编译器字节对齐sizeof(TMDBusTcpHead)不是8吗?”然后我就改掉了。这绝对是我师傅写的时候忘记了,绝对是这个原因(心中窃喜,嘿嘿嘿)。

冷静下来,有看了一遍代码,发现事情并没有这么简单,#pragma pack(push, 1)和#pragma pack(pop)这两行代码,我之前没注意(其实是没用过,不知道就自动忽略了)。然后去上网搜了一下它的用法,发现果然还是太年轻了,师傅就是师傅
-_-.

下面是#pragma pack(push,1)与#pragma pack(1)的用法与区别:

二、#pragma pack(push,1)与#pragma pack(1)的用法与区别:

1. 编译器默认字节对齐:

首先粗略讲一下编译器字节对齐的问题,比如上面的结构体:

1
2
3
4
5
6
7
typedef struct
{
    Uint16 wSerialId;      
    Uint16 wProtocolFlag;
    Uint16 wMsgLen;
    Uint8  byDevAddr;
}TMDBusTcpHead;`

若不用#pragma pack(1)和#pragma pack()括起来,编译会按默认方式对齐(成员数据中的数据类型最大的对齐)。即按照Uint16(2字节对齐)
字节(Uint16)对齐,即sizeof(TMDBusTcpHead) = 8;

#pragma pack(push,1)与#pragma pack(1)的作用是给编译器用的参数设置,关于结构体内存的字节对齐,#pragma pack是指定数据在内存中的对齐方式。

2. #pragma pack (n) 与pragma pack(push,1)的区别

1
2
3
4
5
#pragma pack(n)             //作用:C编译器按照n个字节对齐
#pragma pack()              //作用:取消自定义对齐方式

#pragma pack(push, 1)       //作用:是指把原来对齐方式设置压栈,并设新的对齐方式设置为一个字节对齐
#pragma back(pop)           //作用:恢复对齐状态

#pragma pack(push, 1)可能会比前者更优一点,但是二者其实也没有什么大的区别,比如:

1
2
3
#prama pack(push)           //保持字节对齐状态
#pragma pack(4)             //设定为4字节对齐
相当于:#pragma  pack (push,4)