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

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

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

如下协议:

c语言结构体在 ios蓝牙socket协议的妙用_第1张图片

可以构建:

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的结果,必须是其内部最大成员的整数倍,不足的要补齐。

)详细的后面会提到

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

c语言结构体在 ios蓝牙socket协议的妙用_第2张图片

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

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;

下面比较下面的协议:

c语言结构体在 ios蓝牙socket协议的妙用_第3张图片

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

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协议的妙用)