一种巧妙的处理固定长协议的方法

本文深入探讨了C语言结构体的内存对齐原则,包括数据成员对齐、结构体作为成员对齐以及结构体总大小对齐,并通过CASE1和CASE2两个实例解释了结构体占用空间的计算。理解这些原则有助于更好地理解和解析数据结构。此外,文章还介绍了结构体对齐在协议解析中的应用,以一个包含帧头、设备地址等元素的协议为例,展示了如何利用结构体解析固定长度的数据包。
摘要由CSDN通过智能技术生成

举个栗子

在正式开始前,我们先通过一个简单的栗子,理解一个知识点,这里先卖下关子,可以慢慢往下看.
有如下代码块:

typedef struct {
    char a;

    int b;

    short c;
}CASE1;

上述结构体CASE1中包含:

  • 1个字节长度的char类型的a;
  • 4个字节长度的int类型的b;
  • 2个字节长度的short类型的c.

不考虑其他因素的话,CASE1占用的空间应该是7个字节.
但是因为编译器要对数据成员在空间上进行对齐,由于结构体自身对齐值取数据成员中自身对齐值的最大值,即取4,并且0x00~0x0C的12字节空间满足12 % 4 = 0,所以sizeof(struct CASE1)的值为12.

那么这到底是为什么呢?甩锅给编译器,咱就不管了吗?嗯,还真不懂!好的本篇文章到此结束!@!



能不能讲点能听懂的呢?emmm…那就从另一个知识点来理解下这个原理吧.

C语言的结构体对齐原则

  • 数据成员对齐规则
    结构体的数据成员中,第一个成员从offset为0的地址开始,以后每一个成员存储的起始地址为该成员大小的整数倍(win32中int为4字节对齐).
  • 结构体作为成员
    如果一个结构体b作为结构体a的数据成员,则在结构体a中的结构体1要从内部成员最大的整数倍地址开始存储.
  • 结构体的总大小
    结构体的总大小为该结构体内部最大基本类型的整数倍,不足的要补齐,而不是所有成员的大小总和.

在理解了结构体对齐原则之后,我们使用原则1和原则3就可以轻松的计算出,CASE1的长度为12.

那么再取一个栗子:

typedef struct {
    char a;

    short b;

    int c;
}CASE2;

如果没有搞懂上述的三个原则,那么必会有人认为sizeof(struct CASE2)的大小理解为12,但是通过原则1和原则3,我们可以清晰的计算出,CASE2的大小为8.

我们在stm32中运行一下代码测试一下:

//这里测试的是第一个栗子啊
typedef struct {
    char a;

    int b;

    short c;
}CASE2;

int main(void ){
    CASE2 Case;

    printf("&Case.a %d\n", (unsigned int )(void *)&Case.a);
	printf("&Case.b %d\n", (unsigned int )(void *)&Case.b);
	printf("&Case.c %d\n", (unsigned int )(void *)&Case.c);

    return 0;
}

得到的结果是

    &Case.a 536873208
    &Case.b 536873212
    &Case.c 536873216

可以看出,结果和我们所分析对齐原则是一致,那当我知道了结构体的数据是如何存储的时候,还怕不好解析数据吗?

结构体对齐在协议解析中的用法

干货来了!!!
(点题了)
再举一个栗子:
假设有如下的协议:

帧头设备地址命令字参数1参数2帧尾
EF68

协议由帧头、设备地址、命令字、参数1/2及帧尾组成,假设每个参数都为1个字节固定长度,那么我们可以建立如下数据结构:

typedef struct{
    uint8_t frame_head;
    uint8_t frame_addr;
    uint8_t frame_cmdtype;
    uint8_t frame_param1;
    uint8_t frame_param2;
    uint8_t frame_tail;
}Struct_Frame;

当设备接收完一整包数据时,可使用如下方法进行解析:

int frame_decode(void *buff){

    if(buff == NULL)
        return -1;
    
    Struct_Frame *frame = (Struct_Frame buff);

    printf("frame_head = %d\r\n", frame->frame_head);
    printf("frame_addr = %d\r\n", frame->frame_addr);
    //...

    return 0;
}

若此时设备接收到的数据是0XEF1201020368的话,此时调用frame_decode便可以轻松的将数据解析出来.

结果为:

    frame_head = EF
    frame_addr = 12
    ...

这种方法对于解析定长数据非常方便,但前提是对数据的组成及排序非常清楚,对于不定长协议的解析,嗯嗯,暂时还没想出什么特别好的方法,不过方法肯定有,只是我没发现,以后发现方法的话,会再写篇小作文.

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值