C语言结构体对齐的陷阱
在通信协议的开发过程中,有时会利用结构体构造报文某些字段,这就有可能会涉及到结构体对齐的问题。如果稍有不慎,会因结构体使用不当导致功能受到严重影响。
以下面一段代码为例:
#include <stdio.h>
#define PKT_TIMESTAMP_LEN 8
typedef struct pktIdentifyTlv_s
{
unsigned char type; /*type = 0xfe*/
unsigned char len[2]; /*len = 0x09*/
unsigned char reserved; /*四字节对齐*/
unsigned char operID[4];
unsigned char identify[2]; /*报文标识*/
unsigned char highTransID[2]; /*四字节对齐*/
} pktIdentifyTlv_t;
typedef struct pktTimeStampData_s
{
unsigned char timeStamp1[PKT_TIMESTAMP_LEN]; /*T1*/
unsigned char timeStamp2[PKT_TIMESTAMP_LEN]; /*T2*/
unsigned char timeStamp3[PKT_TIMESTAMP_LEN]; /*T3*/
unsigned char timeStamp4[PKT_TIMESTAMP_LEN]; /*T4*/
}pktTimeStampData_t;
typedef struct cfmCommonHeader_s
{
unsigned char mdLevel_ver; /*MD等级-前3位(0-7),MD版本-0*/
unsigned char opcode; /*详见Y.1731协议*/
unsigned char flags; /*标记(0)*/
unsigned char firstTLVoffset; /*第一个TLV的偏移(4)*/
} cfmCommonHeader_t;
typedef struct dmmPdu_s
{
cfmCommonHeader_t stCfmComHeader; /*CFM COMMON头*/
pktTimeStampData_t stTimeStampData; /*时间戳信息*/
pktIdentifyTlv_t stIdentifyTlv;
unsigned char endTlv;
}dmmPdu_t;
typedef struct counterData_s
{
long TxFCf; /*远端发送帧计算*/
long RxFCf; /*远端接收帧计算*/
long TxFCb; /*LMR帧传输时本地计数器TxFCl的数值*/
}counterData_t;
typedef struct lmmPdu_s
{
cfmCommonHeader_t stCfmComHeader; /*CFM COMMON头*/
counterData_t stCounterData; /*计数器信息*/
pktIdentifyTlv_t stIdentifyTlv;
unsigned char endTlv;
}lmmPdu_t;
int main()
{
int lmm = sizeof(lmmPdu_t);
int dmm = sizeof(dmmPdu_t);
int com_head = sizeof(cfmCommonHeader_t);
int lmm_cnt = sizeof(counterData_t);
int dmm_cnt = sizeof(pktTimeStampData_t);
int id_tlv = sizeof(pktIdentifyTlv_t);
printf("\r\nlmm=%d, dmm=%d\r\n", lmm, dmm);
printf("\r\ncom_head=%d, id_tlv=%d\r\n", com_head, id_tlv);
printf("\r\nlmm_cnt=%d, dmm_cnt=%d\r\n", lmm_cnt, dmm_cnt);
return 0;
}
上述例程在windows XP上C Free 5.0中的运行结果为:
从结果中可以看到,结构体lmmPdu_t按照成员的最大宽度long型进行对齐(按照4字节对齐,编译器对结构体进行了padding),而dmmPdu_t由于成员都是char型(按照1字节对齐,编译器没有对结构体进行padding)。
如果想对lmm报文的stIdentifyTlv字段进行改写,应该从lmmPdu的头部向后偏移24。通过(lmm - 5)计算偏移量,对stIdentifyTlv字段的填充是错误的。
而对于改写dmm报文的stIdentifyTlv字段,可以从dmmPdu的头部向后偏移44,亦可以通过(dmm - 5)计算偏移量,二者的效果是相同的。