• 有符号,无符号数在字节拼接过程中的区别和注意


      最近在学习使用WinSock进行Modbus/TCP的通信,和服务器通信读取寄存器内容时,有部分寄存器的内容是连续的。比如两个16位的寄存器合在一起的32位的数表示的才是一个有意义的量。而TCP传输过程数据都是一个一个字节按大端模式(多字节的数据高位先传输)传输的,这样需要进行将字符拼接。为此测试了下将4个单独字符拼接成一个32为的long类型变量。测试的时候发现字符是signed char还是unsigned char结果有很大的区别。

    1)开始的时候采用的是左移结合相加的方式:

    #include "stdafx.h"
    #include <stdio.h>
    int main(int argc,char*argv[])
    {
    char a1,a2,a3,a4;
    long a;
    a1=(char)(0x36);
    a2=(char)(0x8a);
    a3=(char)(0x8c);
    a4=(char)(0x18);
    a=a1;
    printf("%x\n",a);
    a=(a<<8)+a2;
    printf("%x\n",a);
    a=(a<<8)+a3;
    printf("%x\n",a);
    a=(a<<8)+a4;
    printf("%x\n",a);

    unsigned char aa1,aa2,aa3,aa4;
    aa1=(char)(0x36);
    aa2=(char)(0x8a);
    aa3=(char)(0x8c);
    aa4=(char)(0x18);
    a=aa1;
    printf("%x\n",a);
    a=(a<<8)+aa2;
    printf("%x\n",a);
    a=(a<<8)+aa3;
    printf("%x\n",a);
    a=(a<<8)+aa4;
    printf("%x\n",a);


    return 0;

    }

    结果如下图:

      可以发现此char类型的变量合并的时候会出现问题(高字节的会减一),36和8a两个字节拼接的时候变成了358a,而如果是uchar类型则不会有这个问题。

    因此可以肯定是字符的类型造成的。进一步测试可以发现,当待合并的字节的新最高位为1时才会出现这种情况,而不为1则结果正确。比如将36和7a合并,结果就是367a。

    其实问题的本质是在于这里做的是加法,相加的过程都是先做强制类型转换,将字符型转换为long类型,在进行运算。而有符号数的加法和无符号数的加法不一样。当类型是char时,最高为为1的数据相当于是负数,所以相加的过程,先符号位扩展,高位补1,转换为long类型,然后进行加法运算。而如果是uchar类型,高位补0,再进行加法运算。

    举例说明:

    char:0x10+0x80--->0x0f80

    4096-128=3968=0x0f80

    uchar:0x10+0x80--->0x1080

    4096+128=4224=1080

    其实这个问题主要是合并的方法选的不够恰当。比较合适的字节合并不应该是涉及算术运算,而采用逻辑或更恰当。

    2)后来改用逻辑或运算

    #include <stdio.h>
    int main(int argc,char*argv[])
    {
    char a1,a2,a3,a4;
    unsigned long a;
    a1=(char)(0x36);
    a2=(char)(0x8a);
    a3=(char)(0x8c);
    a4=(char)(0x18);
    a=a1;
    printf("%x\n",a);
    a=(a<<8)|a2;
    printf("%x\n",a);
    a=(a<<8)|a3;
    printf("%x\n",a);
    a=(a<<8)|a4;
    printf("%x\n",a);

    unsigned char aa1,aa2,aa3,aa4;
    aa1=(char)(0x36);
    aa2=(char)(0x8a);
    aa3=(char)(0x8c);
    aa4=(char)(0x18);
    a=aa1;
    printf("%x\n",a);
    a=(a<<8)|aa2;
    printf("%x\n",a);
    a=(a<<8)|aa3;
    printf("%x\n",a);
    a=(a<<8)|aa4;
    printf("%x\n",a);
    return 0;

    }

    结果如下图:

    从结果可以看出,逻辑运算过程中也会有数据类型的强制转变,而有符号字符型扩展成unsigned long,高位全部扩展成1。导致原来的高位全部被1屏蔽了。

    所以这里最好采用无符号数进行扩展运算。或者可以将扩展后数据强制与上0xff,将高位1屏蔽,这样才能得到想要的结果。
    上网查了些关于这方面的资料,发现char和uchar还是区别很大的。一些链接:

    char 与 unsigned char的本质区别

    char ,signed char ,unsigned char

    3)另外在输出十六进制的时候,也发现一个有趣的现象。

    测试代码:

    #include <stdio.h>
    int main(int argc,char*argv[])
    {
    char a1,a2,a3,a4;
    short int a;
    a1=(char)(0x80);
    a2=(char)(0x90);
    a3=(char)(0x70);
    a4=(char)(0x60);
    a=0x8000;
    printf("%x %x %x %x\n",a1,a2,a3,a4);
    printf("%x\n",a);
    }

    测试结果:

    从结果可以看出,printf中,以%x形式输出的默认是int类型32位数据,而如果数据不足32位,则会进行强制类型转换,有符号的数如果是负数,最高位就会全部补1进行扩展,无符号数则不会。因此这里如果数据的最高位为1(有符号则是负数),则结果前面就好有很多1。

      因此在处理数据的时候,虽然数据处理本身可能并不需要考虑最高位时候代表的是符号位(比如网络数据流),但是在调试分析用于显示,数据拼接时,如果不注意可能会造成一些非所愿的结果。

  • 相关阅读:
    钢镚儿和鲨鱼记账的差距
    团队绩效管理
    做什么都队第一段冲刺绩效评比
    第一阶段各组对我们的评价
    第一阶段对各小组的评价
    团队十日冲刺最后一天
    团队十日冲刺第九天
    团队十日冲刺第八天
    满心萧然要坚持更博客鸭
    生而为人
  • 原文地址:https://www.cnblogs.com/followyourheart/p/Followyourheart.html
Copyright © 2020-2023  润新知