提及进制,你们都能想到二进制、10进制、16进制、8进制等等,可是在互联网应用开发中,却不多用到这些换算。在物联网短指令应用中,却十分常见。程序员
理解字节本质和二进制编程
不管是互联网应用仍是物联网应用,在网络传输层传送的其实都是二进制数据。由于现代通讯设备对信号处理都是用的数字电路,数字电路的输入输出只有两种状态,那就是高电平或低电平,也就是对应二进制数据的1和0。按照必定的时序和时钟基准,就能够表明了不一样的信息。理解二进制数据的造成对物联网开发的深刻是颇有必要的。
网络
上图中绿色线表明数据电平,红色线表明用于鉴别二进制每个数据位的时间基准。编程语言
在前面咱们已经说到网络传输中是按照字节传输的,而每一个字节一般是按照8位二进制组成的,那么上图说明了一个字节的数据在数字电路中电平变化状况,也是在网络传输中各个通讯设备间传输的电平变化状况。绿色线是数据线上的电平,能够看到有两个凸起的高电平,高电平表明1,低电平表明0,那么用二进制表示,是否是就是“01010”呢,事实上是不对的。上图的一个字节的二进制值应该是“10011000”,为何会识别多识别出来几个位呢?缘由是还有一个时间间隔的基准参照,就是时钟线上的电平变化。编辑器
每次从低电平变化为高电平,再从高电平变化为低电平,这个过程产生的电平波形是个凸起的方波,咱们称之为高脉冲(因像脉搏跳动同样而得名,也有用低脉冲的,原理相同)。时钟线上接二连三产生着固定时间间隔和脉冲宽度的高脉冲,而数字电路在每一个时钟高脉冲到来的时候,去识别一下数据线上的电平高低,若是是高就表明当前数据为是1,不然为0。就这样每个时钟高脉冲都去识别一次,将结果依次记录下来,就组成了二进制数据。上图中数据线上的第二个高电平被识别成二进制“11”,就是由于在数据线高电平期间经历了两个时钟高脉冲,因此是两个1,而不是一个1,数据线上的低电平被识别成多个0的原理也是同样的。调试
一个字节由8个二进制位组成,一般标准是高位在前,最低位序号是0,最高位序号是7,因此上图中时钟高脉冲上面的数字是描述一个字节中的8个位的序号,也就是顺序。传完一个字节紧接着传下一个字节,原理相同。code
为何叫二进制?咱们知道一般生活中使用的数字计数是10进制的,因逢10进1而得名。那二进制也是由于逢2进1而得名的。一个字节的8位二进制实际就是一个计数标记,由8个位组合在一块儿表示。由于每一个位只能有0和1两种变化,因此要计数到2的时候就得进位了。看下面一组数据:blog
0000 0000 //这个字节的值是0,也就是10进制的0,中间空格为书写描述方便,实际不存在 0000 0001 //这个字节的值是1,也就是10进制的1 0000 0010 //这个字节的值是2,也就是10进制的2,需进位 0000 0011 //这个字节的值是3,也就是10进制的3 0000 0100 //这个字节的值是4,也就是10进制的4,需进位 0000 0101 //这个字节的值是5,也就是10进制的5 ...... 1111 1111 //这个字节的值是255,也就是10进制的255
经过上面一组数据,就能看出逢2进1的关系了,也知道为何一个字节的值是在0-255之间了。那么8个位中,只有一个位为1,其余位为0的时候,分别在不一样位置时,他们的值是怎样的关系呢,看下面一组数据:内存
0000 0001 //这个字节的值是1 0000 0010 //这个字节的值是2 0000 0100 //这个字节的值是4 0000 1000 //这个字节的值是8 0001 0000 //这个字节的值是16 0010 0000 //这个字节的值是32 0100 0000 //这个字节的值是64 1000 0000 //这个字节的值是128
从上面这组数据能够看出,值为1的位,每前进1位,其值就会是以前的2倍。这与10进制的一、十、100等每前进1位值就是以前的10倍是同样的规律,这样更好理解二进制位变化的关系了。那么咱们在代码中,定义一个整数变量,赋值以后,它具体的二进制是什么样的呢,再看下面例子:开发
// C语言代码 unsigned char tmp = 65;
定义了一个无符号8位整数(单字节)变量 tmp,并同时给赋了初值为10进制整数65,运行时这个变量的初值的二进制(也是内存中实际保存的形式)是“0100 0001”,由于这个二进制对应的10进制值就是65。而不是保存6和5这两个数字的ASCii值(54,53)到内存中的。
那么单字节最大值是255,大于255的值是怎么表示的呢,各个语言都有不一样的整数数据类型,他们对应的字节数量是不同的,看下面的例子:
1111 1111 //255,无符号单字节整数最大值 1111 1111 1111 1111 //65535,无符号双字节整数最大值 1111 1111 1111 1111 1111 1111 1111 1111 //4294967295,无符号四字节整数最大值
根据上面这组数据,在你使用的开发语言的数据类型中找到该数据类型是多少个字节的,就知道他能够最大记录的数值是多少了。
对于整数变量是这样记录的,可是字符这类的是如何记录的呢,这就不得不提ASCii码(美国信息交换标准代码)。ASCii码将常见的英文字母、阿拉伯数字、英文标点符号等可见字符和经常使用的非可见控制符号都定义了对应的二进制值,每一个符号占用一个字节,具体可网络搜索“ASCii码”有完整介绍,这里就不用详细介绍了。定义字符变量后,实际的变量二进制值是什么,看下面的例子:
// C语言代码 unsigned char strA = 'A'; //变量二进制值:0100 0001;10进制值:65 unsigned char str1 = '1'; //变量二进制值:0011 0001;10进制值:49
上例中变量str1的值为何是49而不是1,是由于这个‘1’是字符1,也就至关因而咱们写字描述数量用的标识符号1,而不是计数时候的整数值自己,因此要使用字符‘1’的ASCii码值49来表示出来,对应的二进制为“0011 0001”。还有个差异,这个变量的值实际是整数49,可是在系统调试输出的时候,会显示出字符‘1’的,也就是可见的。而真正的那个整数1是须要转换后才能够看到他的值的,不然不可见或看到的是错误的或乱码。大写字母A的ASCii码值是65,因此变量strA实际记录的也是整数65的二进制值。
理解十六机制
理解了字节的本质是二进制组成的,也知道了与十进制的对应关系,已经能够与平常计数换算了,那么十六进制又是什么鬼,为何还要弄出来一个十六进制呢?
首先咱们看二进制的写法,一个字节要写8次,显然很不方便,也很差读,口算成十进制数有很大难度。那么十进制标准书写是两个字符表示一个字节,等宽制的,便于编辑浏览。更重要的是与二进制的换算恰好将8位分红两部分,每4位对应一个字符,两个字符拼在一块儿表明了完整的8位。下面咱们经过数据看一下对应关系:
0000 1010 //16进制为0A,10进制为10 0000 1111 //16进制为0F,10进制为15 0001 0000 //16进制为10,10进制为16 1000 0010 //16进制为82,10进制为130 1111 1111 //16进制为FF,10进制为255
十六进制,顾名思义应是逢16进1才对,但是阿拉伯数字只有0-9,那么逢16进1至少要能表示到15才能够。所以十六进制在0-9的基础上又增长了A-F这6个字母,分别表明10-15。对应10进制值,00表示0,09表示9,0A表示10,0F表示15,10表示16(由于满16就进位了)。上图中把一个字节的8个位四四分开,分别对应先后两个16进制字符,就很快能够换算出来。
第一组前四位都是0,因此十六进制第一个字符写0;后四位是1010,是十进制的10,用十六进制应该是A,因此第二个字符应该是A。这样合起来的十六进制值就是0A。
第二组前四位都是0,因此十六进制第一个字符写0;后四位是1111,是十进制的15,用十六进制应该是F,因此第二个字符应该是F。这样合起来的十六进制值就是0F。
第三组前四位是0001,十六进制值应该是1,因此十六进制第一个字符写1;后四位都是0,用十六进制应该是0,因此第二个字符应该是0。这样合起来的十六进制值就是10。
第四组前四位是1000,十六进制值应该是8,因此十六进制第一个字符写8;后四位是0010,用十六进制应该是2,因此第二个字符应该是2。这样合起来的十六进制值就是82。
第四组前四位是1111,十六进制值应该是F,因此十六进制第一个字符写F;后四位是1111,用十六进制应该是F,因此第二个字符应该是F。这样合起来的十六进制值就是FF,也就是十进制的255,单字节的最大值。
十六进制在不一样的编程语言里面,标识符(用于标注声明这是16进制的数字)有所不一样,在C语言里用“0x”标识,以下面代码:
// C语言代码 unsigned char a = 0xB3; //变量a等于十进制的179 unsigned char b = 0x0C; //变量b等于十进制的12
关于十六进制字面量的表示方法请查阅各语言的官方手册,这里再也不 一一列举。
关于数据值的沟通
在咱们最初的物联网开发团队中,嵌入式团队与服务端团队沟通时,常常出现这样的发问“你那边是发送的16进制的数据仍是10进制的数据啊”,或相似“你是以16进制形式发送的仍是以10进制形式发送的啊”、“你用16进制发的,我也得转成16进制啊,能不能你改为10进制发送啊”等等。
为何会有上面的问题,上面的问题应该这样问吗?后来我发现从互联网开发转过来的程序员多数会有这个问题,这个问题是不该该这样问的,究其缘由主要有一下几种:
一、嵌入式开发人员涉及到数据的底层操做和编辑,多数都是习惯用16进制描述数据,数据编辑器也多数都是16进制的,不多有10进制的。因此沟通的时候,16进制的数值描述首先从嵌入式人员说出来了。
二、因为上面缘由,嵌入式的文档中描述协议标志的时候,也是用16进制进行定义的,这方便数据编辑(后面会讨论到),因此在讨论文档的时候也会出现16进制的描述。
三、互联网开发人员多数习惯了10进制的数值描述,对进制转换和字节的本质因长期不涉及相对底层的开发,就搞不清之间的关系了。
四、最重要的是问的人对数据传输的字节底层含义已经淡忘了,因此一时不能适应了。
其实在沟通中,发送方只要告诉接收方发送的是哪一个进制的多少值就能够了。接收方用16进制读取仍是10进制读取决定与本身,由于数据自己就是那个二进制没变。你读的进制不一样,值的面量就不一样了,根据须要本身选择换算就能够了。例以下面的代码:
// C语言代码 unsigned char a = 0x0B; //16进制赋值 unsigned char b = 11; //10进制赋值 if(a == b){ printf('OK'); }else{ printf('NO'); }
上面代码执行后,控制台输出的是“OK”,由于十六进制的 0x0B 和十进制的 11 的二进制是同样的。
本节完,待续......