美文网首页
c语言结构体在 ios蓝牙socket协议的妙用

c语言结构体在 ios蓝牙socket协议的妙用

作者: Rain_haha | 来源:发表于2018-03-21 15:11 被阅读0次

    ios中不一定需要c构建协议。但是总体来说用c语言来说相对对数据处理友好方便一些,不像oc那样笨重,毕竟c是做底层的。但是用了c处理有时候需要考虑内存释放问题。话不多说。。。

    如果某些协议中只出现8位的数据,这种是相对最好处理的。例如:

    如下协议:

    可以构建:

    typedef struct{

        uint8_t head;

        uint8_t data_1;

        uint8_t data_2;

        uint8_t place[2];

        uint8_t check;

        uint8_t tail;

    }Test_struct;

    对于这种类型的数据要转成oc的NSdata对象可用

    Test_struct struct_data_context;

    NSData *data = [NSData dataWithBytes:&struct_data_context length:sizeof(Test_struct)];

    那么NSdata转该类型的结构体可用

    Test_struct test_struct_data ;

    [data getBytes:&test_struct_data length:sizeof(Test_struct)];

    是不是很简单?不用一个一个赋值了!

    但是。。。如果数据中存在16位数据 (数据分高低8位)这种情况下需要注意一下几点

    1.数据大小端

    2.结构体对齐(

    原则1、数据成员对齐规则:结构(struct或联合union)的数据成员,第一个数据成员放在offset为0的地方,以后每个数据成员存储的起始位置要从该成员大小的整数倍开始(比如int在32位机为4字节,则要从4的整数倍地址开始存储)。

    原则2、结构体作为成员:如果一个结构里有某些结构体成员,则结构体成员要从其内部最大元素大小的整数倍地址开始存储。(struct a里存有struct b,b里有char,int,double等元素,那b应该从8的整数倍开始存储。)

    原则3、收尾工作:结构体的总大小,也就是sizeof的结果,必须是其内部最大成员的整数倍,不足的要补齐。

    )详细的后面会提到

    例如:以下协议格式(小端)

    我们可以这样构建结构体,

    typedef struct{    uint8_t head;    uint8_t data_1;   uint16_t data;    uint8_t place[2];    uint8_t check;    uint8_t tail;}Test_struct;

    注意删除线。对于16位数据小端处理的数据,(上表第3第4字节)ios 处理器arm架构 默认处理数据都是小端传输的, uint16_t 数据映射到数组里就是低8位在前,高8位在后 形如{低8位,高8位 } 的数组,到这里大家是不是觉得很简单。NO NO NO,too 🐑 了。。。。

    莫急,我们可以测试打印的 sizeof (Test_struct)= 8;

    下面比较下面的协议:

    我们如果按照上面逻辑我们会这样构建

    typedef struct{    uint8_t head;  uint16_t data;    uint8_t place[2];    uint8_t check;    uint8_t tail;}Test_struct;

    但是如果你打印一下 size 的话,你会发现sizeof(Test_struct)还是等于 8;明明比上面少一位怎么还是8位?

    原因是 根据结构体原则。第二位成员2个字节,第二位成员内存从结构体开始 2字节的倍数位开始排。所以 第一个成员占了第1字节 还有个补位第二个字节,uint16个低8位占了第三个字节, uint16个高8位占了第四个字节。加上后面的4个字节,一共8个字节,第三原则(最大成员字节的整数陪)总字节数8的确是 最大成员2字节的整数倍。

    那这样的话我们怎样构建结构体方便些呢?

    1 。第一种把所有结构体 改成一个字节 ,意思是把uint16 高低字节拆开。(常规做法)。

    2。结构体补位

    第一种就不用说了,我们试下第二种怎么做。

    typedef struct{ uint8_t head; uint8_t space; uint16_t data; uint8_t place[2]; uint8_t check; uint8_t tail; }Test_struct;

    注意删除线。我们在head 后面补了一位。补了一位数据自然有出入,莫急。我们最后生成的时候把这一位删除数据就对了。当然方法有很多(数组遍历赋值等等),我就用内存拷贝补位前和补位后的内存这种方法。

    NSData * struct2NSdata(Test_struct *context) {

            int temp_size = sizeof(Test_struct) - 1; //有效的数据字节数

            uint8_t  *temp_context = (uint8_t *)context; //强转结构体指针类型,为了后面一个字节的偏移量

            uint8_t *temp = malloc(temp_size);//分配内存

            memset(temp, 0, temp_size); //内存块初始化0

            memcpy(temp, temp_context, 1);//拷贝第一位;

            memcpy(temp + 1,temp_context + 2, temp_size - 1); //拷贝context第3位至结尾

            NSData *result = [NSData dataWithBytes:temp length:temp_size]; //最终生成的data

            free(temp); //释放开辟的内存

            return result;

    }

    对于NSData 转结构体也是一个办法补一位 哈哈

    void nsdata2Struct(NSData *data , Test_struct *context) {

            int temp_size = data.length; //有效的数据字节数

            uint8_t *temp_context = context; //强转结构体

            uint8_t *head_data = malloc(1);

            [data getBytes:head_data range:NSMakeRange(0, 1)]; //储存前内存块

            uint8_t *tail_data = malloc(temp_size - 1);

            [data getBytes:head_data range:NSMakeRange(1, temp_size - 1)]; //储存后面内存块

            memcpy(temp_context, head_data, 1); //拷贝前内存

            memcpy(temp_context + 2, tail_data, data.length - 1); //拷贝后面的内存块 到第三字节至结尾 (忽略第二位)

            free(head_data); //释放

            free(tail_data);

    }

    还有结构体 第三原则 必须是最大成员的整数倍,如果不是会在结构体最后一位补位。 处理方法也和上面类似。

      综上,如果数据都是一个字节数据,构建可以什么都不用考虑,

     如果数据为小端传输,可以用上面方法,考虑一下结构体对齐,不对齐需要补位,优点是,数据转换的时候不需要一个个赋值,反正我是写的手疼。 

    如果数据为大端传输,还是老老实实把成员都变成一个字节的成员变量,或者写个函数高低位转换。在用上面所提的方法。

    相关文章

      网友评论

          本文标题:c语言结构体在 ios蓝牙socket协议的妙用

          本文链接:https://www.haomeiwen.com/subject/daquyxtx.html