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

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

  最近在学习使用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/archive/2011/10/24/Followyourheart.html

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请联系我们举报,一经查实,本站将立刻删除。

发布者:全栈程序员-站长,转载请注明出处:https://javaforall.net/110529.html原文链接:https://javaforall.net

(0)
全栈程序员-站长的头像全栈程序员-站长


相关推荐

  • matlab经典案例_小苹果最炫民族风串烧

    matlab经典案例_小苹果最炫民族风串烧%Mostshiningnationalwind//最炫民族风onMatlab%TheModificationisfrom”canon”,notbymefs=44100;%sampleratedt=1/fs;T16=0.125;t16=[0:dt:T16];[tempk]=size(t16);t4=linspace(0,4*T16,4*k…

    2022年9月22日
    4
  • idea激活插件(在线激活)

    idea激活插件(在线激活),https://javaforall.net/100143.html。详细ieda激活码不妨到全栈程序员必看教程网一起来了解一下吧!

    2022年3月14日
    728
  • java线程池的面试题_献给准备面试的你,Java线程and线程池面试题小结「建议收藏」

    java线程池的面试题_献给准备面试的你,Java线程and线程池面试题小结「建议收藏」最近这几天一直在整理Java相关的面试题,“金九银十”是求职的最佳时间,但是现在的“银十”也已经过去了一半的时间,相信现在还在为面试四处奔波的小伙伴已经很疲惫了吧,下面就来减轻你负担,Java线程和线程池相关的面试题整理给大家,减轻你准备面试的负担。丑话说在前面,我“丑”我先说,嘿嘿。因为篇幅有限,所以这次的文章不会包含面试题的所有的内容,在这里求大家点一波关注啦!以后会持续更新哒!1、为什么用线…

    2022年5月22日
    36
  • OWASP TOP10系列之#TOP1# A1-注入类「建议收藏」

    OWASP TOP10系列之#TOP1# A1-注入类「建议收藏」OWASPTOP10系列之#TOP1#注入类提示:本系列将介绍OWASPTOP10安全漏洞相关介绍,主要针对漏洞类型、攻击原理以及如何防御进行简单讲解;如有错误,还请大佬指出,定会及时改正~文章目录OWASPTOP10系列之#TOP1#注入类前言一、注入类漏洞是什么?二、什么情况下会产生注入类漏洞问题?三、如何预防?四、具体示例1.SQL注入2.OS命令注入3.XPath注入总结前言在OWASP(开放式Web应用程序安全项目)公布的10项最严重的Web应用程序安全风险列表的在

    2022年5月15日
    34
  • java web项目中hibernate导入问题解决, AbstractInterceptor

    java web项目中hibernate导入问题解决, AbstractInterceptor解决步骤如下:先说一句:修改后先保存,然后看看错误消失没?只需要导入正确的包properties-&gt;javabuildpath-&gt;Libraries-&gt;AddLibaray-&gt;Myeclipselibrary-&gt;你需要导入的包-&gt;applyimportcom.opensymphony.xwork2.Action;importcom.opensym…

    2022年5月14日
    39
  • 黑客入门 | 什么是踩点[通俗易懂]

    黑客入门 | 什么是踩点[通俗易懂]踩点就是收集目标信息的技巧,通过踩点找寻你感兴趣的信息。《孙子兵法》曰:“知已知彼,百战不殆;不知彼而知己,一胜一负;不知彼不知己,每战必殆。”通过对目标进行有计划,有步骤的踩点,收集整理出一份关于该目标的信息安防现状完整剖析图。收集方面:因特网、内联网、远程访问、外联网因特网主要收集:域名、网络地址和子网、可以直接从因特网访问的各个系统的具体IP地址、已经被发现的各个系统上运行的TCP和…

    2022年5月15日
    109

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

关注全栈程序员社区公众号