第 9 节

数据的存放

  1. 数据单位及其转换
    1. 1字节byte = 8比特bit = 8位二进制
    2. 数据是以 二进制 的形式存在电脑内存中的
    3. 进制转换
      1. 在C语言中,0b开头的数据代表2进制,0x开头的数代表16进制。因为2进制较长,所以我们在代码中,一般把2进制转化成16进制进行表示。
    4. 数据的原码
      1. 无符号(unsigned)的数据类型: 无符号数据所占的所有2进制都表示数据的大小,假设占8位2进制,那么该数最大是255(其对应的2进制:1111 1111),最小是0(对应2进制是0000 0000)。
      2. 有符号(signed)的数据类型:有符号的数据其最高位二进制是符号位,符号位是0则是正数,符号位是1则为负数。假设占8位2进制,去掉1位2进制,还剩7位2进制来表示数据的大小。那么其最大是127(其2进制是0111 1111),最小是-128(其2进制是1000 0000,-127的是1111 1111),这个-128其实是用了-0的二进制表示,因为咱们有0(0000 0000)了,所以-0(理论是1000 0000)就没有啥意义了,所以我们规定把-0的二进制表示成-128。
    5. 数据的命名 :
      1. 由于unsigned char,int等数据类型名称无法突出 数据的二进制 有无符号,占多少二进制,所以我们用typedef给其起新的别名(比如 新别名 uint8_t,int32_t)
        1. uint8_t 中的u代表unsigned无符号,8代表占8位二进制,也就是1个字节。
        2. int32_t前面不带u,所以是有符号的,占32位二进制,所以也就是占4个字节,所以是int类型的别名
        3. 因为char 占 1 字节,8 比特,8 位二进制,而且有符号,所以我们叫他int8_t
        4. 因为unsigned short int 无符号,占 2字节,16比特,16位2进制,所以叫uint16_t,其他的类似。
        5. float占4字节,32位二进制,所以叫fp32
        6. double占8字节,64位二进制,所以叫fp64
      2. 具体的代码(在C++中自带一部分别名,可以不写一部分别名,但建议写):
        1. typedef unsigned char uint8_t;
        2. typedef short int int16_t;
        3. typedef unsigned int uint32_t;
        4. typedef float fp32;

  2. 数据解析:
    1. 应用场景介绍:一般传感器会一直给我们的单片机、工控机发送数据,比如说发送我们机器人所走的路程(假设单位是毫米,是个短整型数)。
    2. 数据处理目标: 比如获取路程(短整型,2个字节,16位2进制)
    3. 传感器传数据: 一般的通信来说,传感器会一个字节一个字节的给单片机和工控机不断发数据,一般一个占n字节的物理量数据,要分成n个字节,也就是n个变量来发,又因为数据以2进制的形式存储,一般先发最高的8位二进制。
    4. 单片机、工控机接受到的数据: 比如获取机器人的路程,该数据占2字节,所以传感器要分成两个变量来发送。先接收到的变量我们叫DH(数据的高8位2进制),后接收到的数据我们叫DL(数据的低8位2进制)。
    5. 数据处理思路: 我们现在拥有的是两个普通的8位二进制变量(uint8_t类型的),我们想要获得带符号的16位2进制的数据,机器人的路程(int16_t类型的)。
      1. 位操作 : 假如说,DH是0x9D(对应2进制是1001 1101),DL是0x57(对应2进制是0101 0111)。那么我们想要的16位数据就是把DH当16位中的最高的8位,DL当最低的8位,也就是我们想要的数据DATA(其二进制是 1001 1101 0101 0111)。我们想实现这个效果,就需要位操作,把DH向左移8位,让他变成1001 1101 0000 0000,然后再把左移后的DH 与 DL进行按位或运算,也就是1001 1101 0000 0000 与 0000 0000 0101 0111进行按位或,最后就得出来DATA是1001 1101 0101 0111了。
      2. 强转类型: 我们上面已经得出来data的2进制了,再进行强转化成有符号的数,也就是把最高位中的1变成符号位,具体代码是int16_t DATA = (int16_t) ((uint16_t)DH << 8 | DL ),这样就处理成功了。
  3. 数据在内存中的二进制表示方式
    1. 计算机存储数据的前置知识
      1. 计算机底层存储数据时使用的是二进制数字,但是计算机在存储一个数字时并不是直接存储该数字对应的二进制数字,而是存储该数字对应二进制数字的 补码
      2. 机器码:一个数在计算机的存储形式是二进制数,我们称这些二进制数为机器数,机器数是有符号,在计算机中用机器数的次最高位的左侧存放符号位,0表示正数,1表示负数
      3. 真值:因为机器数带有符号位,所以机器数的形式值不等于其真实表示的值(真值),以机器数1000 0001为例,其真正表示的值(首位为符号位)为-1,而形式值(首位就是代表1)为129; 因此将带符号的机器数的真正表示的值称为机器数的真值
    2. 原码、反码、补码:
      1. 原码 的表示与机器数真值表示的一样,即用第一位表示符号,其余位表示数值。也就是

          正数:就是它对应的二进制数。 负数:将绝对值对应的二进制最左边位变为1。
      【+1】= 原:[ 0000 0001 ]
      【-1】= 原:[ 1000 0001 ]
      
      1. 反码:

          正数 : 和原码相同。 负数 : 在其原码的基础上,符号位不变,其余各位取反。
      【+1】= 原: [ 0000 0001 ] = 反:[ 0000 0001 ]
      【-1】= 原: [ 1000 0001 ] = 反:[ 1111 1110 ]
      
      1. 补码

          正数 : 补码是其原码本身。 负数 : 补码是在其原码的基础上,符号位不变,其余各位取反后加1(即在反码的基础上加1)
      【+1】= 原: [ 0000 0001 ] = 反:[ 0000 0001 ] = 补:[ 0000 0001 ]
      【-1】= 原: [ 1000 0001 ] = 反:[ 1111 1110 ] = 补:[ 1111 1111 ]
      
    3. 数据在计算机中的存储形式
      1. 计算机实际只存储补码,所以原码转换为补码的过程,也可以理解为数据存储到计算机内存中的过程:

      1. 正数:在原、反、补码中,正数的表示是一模一样的
      2. 负数:负数的表示是不相同的,所以对于负数的补码来说,我们是不能直接用进制转换将其转换为十进制数值的,因为这样是得不到计算机真正存储的十进制数的,所以应该将其转换为原码后,再将转换得到的原码进行进制转换为十进制数(机器数包含符号位)
      3. 为什么要诞生原码、反码和补码?
        1. 原因:对于只有正数的加减运算来说,用原码计算是没有任何问题的,但是当既有正数,也有负数的时候,计算机无法判断最高位是否是符号位。
    4. 总结:
      1. 二进制的最高位是符号位:0表示正数,1表示负数
      2. 正数的原码反码补码都一样,三码合一。
      3. 负数
        1. 负数的反码 = 它的原码符号位不变,其它位取反。
        2. 负数的补码 = 它的反码 + 1, 负数的反码 = 负数的补码 - 1 。
      4. 0 的反码、补码都是 0 。
      5. 在计算机运算的时候都是以 “补码” 的方式来运算的。
      6. 运算结果是以 原码 形式展现的。