由字节对齐引发的一场“血案“
最近在搞个网络通信协议,
采用socket udp传输,
运行时,居然报段错误了,
经过debug,发现居然是因为字节对齐问题导致的。
这个问题在实现通信协议,是经常会遇到的问题,
为了方便读者理解,
我把内容做了简化,分享给大家。
1、协议说明
通信协议信令格式如下:
typedef struct protocol_msg_s{
UINT8 msgType;
UINT8 data1;
UINT8 data2;
UINT16 len;
char data[100];
}PRO_MSG;
根据协议格式,我造了一个数据frm,代表我收到的某个信令,
UCHAR frm[]={0x12,0x34,0x56,0x00,0x07,0x01,0x02,0x03,0x04,0x05,0x06,0x07};
根据协议,
信令的字段与原始帧对应关系如下
于是我实现了一个简单的解析代码【该代码有问题】
int main(int argc, char **argv)
{
int ret;
int frm_len = 0;
UINT8 frm[]={0x12,0x34,0x56,0x00,0x07,0x01,0x02,0x03,0x04,0x05,0x06,0x07};
PRO_MSG *pmsg = (PRO_MSG *)frm;
printf("devType:%02x data1:%02x data2:%02x len:%04x \n",
pmsg->msgType,
pmsg->data1,
pmsg->data2,
pmsg->len);
}
编译运行后,其中len的值居然是0107,而不是0007
这其实就是因为编译器采用了字节对齐导致的,
在给pmsg->len赋值时,因为需要2个字节,
这两个字节是frm[3]、frm[4]
这正好分布在两个字里,
编译器忽略了frm[3],最终将frm[4]、frm[5]合在一起赋值给了pmsg->len
为什么有字节对齐?
简单的说内存对齐能够提高 cpu 读取数据的速度,减少 cpu 访问数据的出错性(有些 cpu 必须内存对齐,否则指针访问会出错)。
原因找到了,下面就是解决了。
2、解决办法
1. 方法1 #pragma pack()
该预处理指令用来改变对齐参数。在缺省情况下,C编译器为每一个变量或数据单元按其自然对界条件分配空间。一般地,可以通过下面的方法来改变缺省的对齐参数:
使用伪指令#pragma pack (n),C编译器将按照n字节对齐。
使用伪指令#pragma pack (),取消自定义字节对齐方式。
完整代码
#include <stdio.h>
#include <string.h>
typedef unsigned char UINT8;
typedef unsigned short UINT16;
#define MAX_FRM_DATA_LEN 100
#pragma pack(1)
typedef struct protocol_msg_s{
UINT8 msgType;
UINT8 data1;
UINT8 data2;
UINT16 len;
char data[MAX_FRM_DATA_LEN];
}PRO_MSG;
#pragma
int main(int argc, char **argv)
{
int ret;
int frm_len = 0;
UINT8 frm[]={0x12,0x34,0x56,0x00,0x07,0x01,0x02,0x03,0x04,0x05,0x06,0x07};
PRO_MSG *pmsg = (PRO_MSG *)frm;
printf("devType:%02x data1:%02x data2:%02x len:%04x \n",
pmsg->msgType,
pmsg->data1,
pmsg->data2,
pmsg->len);
}
2. 方法2
老老实实将收到的数据帧逐字节解析,
并填充到struct protocol_msg_s
#include <stdio.h>
#include <string.h>
typedef unsigned char UINT8;
typedef unsigned short UINT16;
#define MAX_FRM_DATA_LEN 100
typedef struct protocol_msg_s{
UINT8 msgType;
UINT8 data1;
UINT8 data2;
UINT16 len;
char data[MAX_FRM_DATA_LEN];
}PRO_MSG;
int frm_parse(PRO_MSG *pmsg,UINT8 buf[],int len)
{
int pos=0;
pmsg->msgType = buf[pos];
pos++;
pmsg->data1 = buf[pos];
pos++;
pmsg->data2 = buf[pos];
pos++;
pmsg->len = buf[pos]<<8 | buf[pos+1]<<0;
pos+=2;
if(pmsg->len>MAX_FRM_DATA_LEN)
{
printf("frm len is longer than 100\n");
return -1;
}
memcpy(pmsg->data,&buf[pos],pmsg->len);
return 0;
}
int maini(int argc, char **argv)
{
int ret;
int frm_len = 0;
UINT8 frm[]={0x12,0x34,0x56,0x00,0x07,0x01,0x02,0x03,0x04,0x05,0x06,0x07};
PRO_MSG msg;
PRO_MSG *pmsg = &msg;
frm_len = sizeof(frm);
ret = frm_parse(pmsg,frm,frm_len);
if(ret<0)
{
printf("frm_parse fail\n");
return -1;
}
printf("devType:%02x data1:%02x data2:%02x len:%04x \n",
pmsg->msgType,
pmsg->data1,
pmsg->data2,
pmsg->len);
return 0;
}
欢迎关注公众号:一口Linux