3个bytes, 怎么接?

这个问题,对于熟悉C语言的人来说,答案很简单。

不过对我这种不熟悉C语言的人,在坑中「摸索」良久,先后尝试好几种方法。

其实,生活中很多事情也像编程:解决问题的办法有万千,但某些方法确实是比较优雅的。

在这个「摸索」的过程中,也是一个蛮有趣的过程,遂记之。

问题

在过去的项目中,所接触到的「协议/指令(protocol/command)」,数据大多是以1个byte(字节),2个bytes,4个bytes,8个bytes......为单位进行切割组合的。类似如下指令:

xxxCommand:

  • [1]commandID# 0xFF // 方括号数字:该数据所占字节(byte)数
  • [1]week:
    • bit0:Mon
    • bit1:Tue
    • bit2:Wed
    • bit3:Thu
    • bit4:Fri
    • bit5:Sat
    • bit6:Sun
    • bit7:不使用
  • [4]ip address
  • [2]reserved
  • [1]checksum

这时候,1 byte的数据,用UInt8接,2 bytes的数据,用UInt16接,4 bytes的数据,用UInt32接——一切都很美好。

关于UInt8UInt16UInt32等数据类型,在MacTypes.h中,有相关说明:

/********************************************************************************

    Base integer types for all target OS's and CPU's
    
        UInt8            8-bit unsigned integer 
        SInt8            8-bit signed integer
        UInt16          16-bit unsigned integer 
        SInt16          16-bit signed integer           
        UInt32          32-bit unsigned integer 
        SInt32          32-bit signed integer   
        UInt64          64-bit unsigned integer 
        SInt64          64-bit signed integer   

*********************************************************************************/

也有具体定义:

    typedef unsigned char                   UInt8;
    typedef signed char                     SInt8;
    typedef unsigned short                  UInt16;
    typedef signed short                    SInt16;

而最近,遇到一种新情况:硬件那边发过来的数据,是3个bytes为单位的数据——有3个bytes的mac地址(截取了mac地址的一半,发送/广播给手机端),也有3个bytes的大气压数据。类似如下数据格式:

xxxCommand:

  • [2]UUID
  • [3]mac // 截取了mac地址的一半
  • [3]presure // 据闻大气压数值,2 bytes表示不完,4 bytes又太多了,所以定义了3 bytes~
  • ...

于是,就有了此文的标题:系统没有UInt24,3个bytes的数据,怎么接?(不要怪我问那么白痴的问题)

先贴出我所认为的「最优雅」解决方案,再描述一下我「踩坑」的心路历程。

Solution:

1.先定义一个UInt24

关于如何定一个UInt24,StackOverFlow上有人提问:How to define 24bit data type in C?

尝试过某个回答者的做法:

    struct int24{
        unsigned int data : 24;
    };

经验证,这个写法不work,因为这个类型还是占4个bytes(用sizeof()函数打印验证),这样拿去接数据,会把别人的那个byte也装过来,后面的数据就会乱掉。

那试着仿照MacTypes.h里的定义,定义如下:

typedef unsigned char[3] UInt24;

这样OK吗?事实上,也有问题,系统会报如下错误:

Brackets are not allowed here; to declare an array, place the brackets after the identifier. Replace '[3] UInt24' with ' UInt24'

报错说得很清楚:方括号放错地方。要定义一个array(数组/数列),方括号应该放在新定义类型名称的后面:

typedef unsigned char UInt24[3];

这样就OK了。

2.定义command

有了对应的「容器」装数据,那接下来可以定义command(指令)了:

    typedef struct __attribute__((packed)) {
        UInt16 UUID;
        UInt24 mac; // 用自己定义的UInt24接数据 
        UInt24 pressure;
    } D2MXxxCommand; // D2M: Device to Mobile phone

其实到这里,基本就把问题解决了,后面该干嘛干嘛了。但是在获取到数据,显示出来的过程中,有些写法还是刷新了我的认知(主要还是自己对C语言不熟)。

  • 将mac地址的3个bytes转为十六进制形式的字符串

一开始我用了很复杂的方法,网上查到的方法也大都比较复杂(下面会有叙述)。

而实际上,只需要一行就OK了:

NSString *macHexString = [NSString stringWithFormat:@"%02X%02X%02X", cmd->mac[0], cmd->mac[1], cmd->mac[2]];

正常的占位符应该是%X,而这里中间的02,表示该十六进制数限制固定两位数。

目的是预防这种情况:当第一个byte是小于16的数,只输出1位。例:0x014B5C,如果是用%X,则只输出14B5C;而用%02X,则可输出014B5C

直接用%02X,就无须再额外判断第一个byte长度是否小于1,如果小于1,再在前面补个零……

备注:这个写法,参考了以前公司boss的写法。

  • 将pressure(大气压)的3个byte转为十进制浮点数

比如,我们用UInt24接了一个数:0x0185B2(大气压),要转换为十进制的浮点数:

   // 大气压值 = 十进制值 / 100 
   float pressure = ((cmd->pressure[0]<<16) + (cmd->pressure[1]<<8) + cmd->pressure[2]) * 0.01; 

因为大气压的值,同事定义为:该十六进制数的十进制形式再除以100。所以,思路就是将该3个独立的byte组合成一个完整的数,再转十进制就OK了。

cmd->pressure[0]<<16的意思,就是将pressure中第一个byte左移16bit(位),也就是左移2个byte(字节)的位置——所以操作完后,pressure中第一个byte,从右往左数,就变成是第三个byte了。如下图所示(将0x01往左移16bit(位)):

将0x01往左移16bit(位)

cmd->pressure[1]<<8也做了类似的事情,将pressure中第二个byte左移8bit(位),也就是左移1个byte(字节)的位置,如下图(将0x85往左移8bit(位)):

将0x85往左移8bit(位)

最后把他们加起来,就是我们要的数了:997.62(Hpa)

另外,

float pressure = ((cmd->pressure1 * 65536) + (cmd->pressure2 * 256) + cmd->pressure3) * 0.01

也有同样的效果,但个人认为这样操作,没有用<<操作符直观易懂。

以上,就是关于「3个bytes, 怎么接?」的回答。

接下来描述一下踩过的「坑」。

其他的尝试:

1.定义成3个单独的UInt8

最开始想到,就是单独定义3个UInt8来接数据:

    UInt8 pressure1;
    UInt8 pressure2;
    UInt8 pressure3; 

写完这个还「怨气满满」地想:为什么非得要传3个bytes过来,多一个、少一个不行吗?

这埋怨虽是戏言,但是从「产品、消费者」的角度思考,又可以延伸到另外一件事:我们写的框架、软件、产品,有一个重要的准绳——「把复杂留给自己,把简单留给客户」。大部分人拿到一个东西,肯定希望是「插电即用」的,并不希望东折腾西捣鼓才能使用。

扯远了,继续:拿到这3个bytes后,第一反应就是NSData对象——于是就变着法把这三个bytes捣鼓成NSData对象:

    // 将3个bytes重新组合起来
    Byte pressureBytes[] = {cmd->pressure1, cmd->pressure2, cmd->pressure3};
    // 转为NSData
    NSData *pressureData = [NSData dataWithBytes:pressureBytes length:sizeof(pressureBytes)];

然后又想办法将NSData对象捣鼓成十六进制字符串,或者是十进制的浮点数——硬生生把一行代码搞定的事情,写成了几十行。

2.定义成UInt8 mac[3]

定义成UInt8 mac[3]形式,其实这和最上面定义UInt24是类似的,只是最上面的方法起了一个更易于理解的UInt 24而已。

3.用UInt32接数据,再截前面3个bytes

这种方法也work,不过要注意,UInt32接回来的数据是4bytes,最后一个byte要进行正确处理(正确给到其他需要的地方),否则后面数据的读取全会乱(少一个byte)。

另外,还试过定义成char *mac形式,不work,因为sizeof(cmd->mac)是8,一个指针占用了8 bytes,并不是我们想要的3bytes。

所以,

还要继续熟悉C语言。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 205,033评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 87,725评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,473评论 0 338
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,846评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,848评论 5 368
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,691评论 1 282
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,053评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,700评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 42,856评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,676评论 2 323
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,787评论 1 333
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,430评论 4 321
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,034评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,990评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,218评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,174评论 2 352
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,526评论 2 343

推荐阅读更多精彩内容