【C语言】C语言打印16进制出现0xffffff现象的问题剖析!

今天在博问里面看到一个朋友的问题,大致是在网络程序中,打印出来的16进制数,莫名的出现ffffff。例如,某个byte真是值为0xc9,打印出来确是0xffffffc9。

其实类似的问题不是只在网络程序中才会出现的,看示例代码:

  1 #include <stdio.h>
  2 int main()
  3 {
  4     char c = 0xc9;
  5     printf("A:c = %2x\n",(unsigned char)c);
  6     printf("B:c = %2x\n",c & 0xff);
  7     printf("C:c = %2x\n",c);
  8     return 0;
  9 }
 

  程序输出如下:

A:c = c9
B:c = c9
C:c = ffffffc9

  可以看到:

  把c转换成unsigned char打印是正确的。视作情况A。

  把c与 0xff做&操作后打印正确。视作情况B。

  对c不做任何处理,则问题复现了,打印出ffffffc9。视作情况C。

  情况A B是我百度来的一些解决C现象的方法。那么我们现在来逐一分析解释ABC三种情况。

  

  首先我们必须知道,printf()函数的%x(X)输出的是Int型别的16进制格式。所以char型别的c变量会被转换成Int型别。

  其次,我们的知道计算机是用补码表示数据的。关于原码,反码,补码的知识请自行充电。

情况C:

   c的补码:11001001(0xc9)。

   c的反码:11001000(0xc9)。

   c的原码:10110111(0xc9)。 

   因为char型别是带符号的,所以最高位的1这里视为负号。

   把c转换成Int型别   char  -----> Int

   Int_c的原码:10000000 00000000 00000000 00110111(把c原码的最高位1  提到最高位。其余高位补0)。

   Int_c的反码:11111111 11111111 11111111 11001000

   Int_c的补码:11111111 11111111 11111111 11001001(0xffffffc9)。

   所以打印出来看似诡异的值其实是合情合理的。如何避免?看AB情况。

情况B:

  我们在情况C的基础上将c与0xff做&操作。

  Int_c的补码:11111111 11111111 11111111 11001001(0xffffffc9)。

         &

         00000000 00000000 00000000 11111111

  最终结果为: 00000000 00000000 00000000 11001001(0xc9)。

情况A:

  我觉得情况A的处理方式才是最正规的处理办法,但是据说linux内核使用(&0xff)。

   c的补码:11001001(0xc9)。

   c的反码:11001001(0xc9)。

   c的原码:11001001(0xc9)。 

   这里强制转换c为unsigned char型别。因此最高位的1不是正负号

   把c转换成Int型别   char  -----> Int

   Int_c的原码:00000000 00000000 00000000 11001001(把c原码的最高位1  提到最高位。其余高位补0)。

   Int_c的反码:00000000 00000000 00000000 11001001

   Int_c的补码:00000000 00000000 00000000 11001001(0xc9)。

   因此打印正常。

  以上分析,如有不正确的地方请各位指正。

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
标题 :程序员计算器说明 版本 :0.1.2.10 日期 :2014-02-15 作者 :翁传对 Email:[email protected] [email protected] 一、进制转换 输入 10进制:直接输入,支持负数。 16进制:0x10,不支持负数 浮点数:直接收入,支持负数。 2进制:二进制图标点击输入。 字符:'A','翁',字符必须用两个单引号。 =================================================================================================== 二、表达式计算 1.支持大数运算。大数用数组表示,数组大小为10000个元素。可以计算3502的阶层。 大数表示方法: sign,intcount,decimalcount|num[PBigNum_ValueLength]。 sign: 符号。正数:sign=0; 负数:sign=1。 intcount: 整数个数。 decimalcount: 小数个数。 num: __int64数组,元素个数=PBigNum_ValueLength。 举例1: 0,3,0|0,0,0,0,0,0,0,1,2,3代表123。(假设PBigNum_ValueLength=10) 举例2: 1,3,2|0,0,0,0,0,1,2,3,4,5代表-123.45。 举例3: 0,1,0|0,0,0,0,0,0,0,0,0,0代表0。 举例4: 0,1,0|0,0,0,0,0,0,0,0,0,1代表1。 举例5: 0,0,1|0,0,0,0,0,0,0,0,0,1代表0.1。 举例6: 0,0,0|0,0,0,0,0,0,0,0,0,0 此数非法 特点: sign,intcount,decimalcount,num[]均不可能出现负数;sign取值0与1;intcount和decimalcount不可能同时是0。 --------------------------------------------------------------------------------------------------- 2.支持则运算,支持小括号(不支持中括号和大括号),支持负数,支持双精度浮点数double。 支持+-*/。运算数以数组表示,并模拟+-*/,并没有直接调用C/C++当中的+-*/运算符对两个运算数进行运算。 3.支持以下字符串运算:"123+-456","123--456"。不支持以下字符串运算:"123++456","123-+456"。 4.小数点精度20位。 5.支持pi常数。 ------------------------------------------------- 5.测试用例: 1/6=0.16666666666666666667 3175/6=529.16666666666666666667 1/7=0.14285714285714285714 1+(2)=3 1+(-2)=-1 0xFF+1=256 0xFFFFFFFFFFFFFFFF*0xFFFFFFFFFFFFFFFF=340282366920938463426481119284349108225 -0x123*-0x123=84681 0xFFFFFF+0=16777215 0xFFFFFF*0=0 10.569*2.469=26.094861 12.5+13.5=26 12.5/13.5=0.92592592592592592593 56*0=0 (((952.5*400/25.4)*1024*2)/1024/1024)/8=3.662109375 fac(100)=93326215443944152681699238856266700490715968264381621468592963895217599993229915608941463976156518286253697920827223758251185210916864000000000000000000000000 0.7*0.15=0.105 0.00135375*1还有问题 0.01*1 sin(20)=(20*pi/180)-pow((20*pi/180),3)/fac(3)+pow((20*pi/180),5)/fac(5)-pow((20*pi/180),7)/fac(7)+pow((20*pi/180),9)/fac(9)-pow((20*pi/180),11)/fac(11) 39
以下是一个简单的C#代码示例,可用于JT809协议数据的解析和生成: ```csharp using System; using System.IO; using System.Text; namespace JT809Protocol { public class JT809Message { public uint MsgLength { get; set; } public uint MsgSn { get; set; } public ushort MsgId { get; set; } public byte[] MsgBody { get; set; } public uint CRC32 { get; set; } public void Serialize(Stream stream) { BinaryWriter writer = new BinaryWriter(stream); writer.Write(MsgLength); writer.Write(MsgSn); writer.Write(MsgId); writer.Write(MsgBody); writer.Write(CRC32); } public void Deserialize(Stream stream) { BinaryReader reader = new BinaryReader(stream); MsgLength = reader.ReadUInt32(); MsgSn = reader.ReadUInt32(); MsgId = reader.ReadUInt16(); MsgBody = reader.ReadBytes((int)(MsgLength - 12)); CRC32 = reader.ReadUInt32(); } public static uint CalculateCRC32(byte[] data, uint offset, uint length) { uint[] table = new uint[] { 0x00000000, 0x04C11DB7, 0x09823B6E, 0x0D4326D9, 0x130476DC, 0x17C56B6B, 0x1A864DB2, 0x1E475005, // 略... 0xF40E3585, 0xF060CF14, 0xFED41B76, 0xFA0F3D63, 0xFDBAACF8, 0xFFCCBBE1, 0xFF95C5F4, 0xFF2A7604, }; uint crc = 0xFFFFFFFF; for (uint i = offset; i < offset + length; i++) { crc = (crc << 8) ^ table[((crc >> 24) & 0xFF) ^ data[i]]; } return ~crc; } } public class JT809Serializer { public static byte[] Serialize(JT809Message message) { MemoryStream stream = new MemoryStream(); message.Serialize(stream); byte[] data = stream.ToArray(); message.CRC32 = JT809Message.CalculateCRC32(data, 0, (uint)data.Length); stream.Seek(0, SeekOrigin.Begin); message.Serialize(stream); return stream.ToArray(); } public static JT809Message Deserialize(byte[] data) { JT809Message message = new JT809Message(); MemoryStream stream = new MemoryStream(data); message.Deserialize(stream); uint crc = JT809Message.CalculateCRC32(data, 0, (uint)data.Length - 4); if (crc != message.CRC32) { throw new Exception("CRC32 check failed"); } return message; } } } ``` 上述代码中,`JT809Message`类表示一个JT809消息,包括消息头和消息体。`Serialize`方法用于将消息序列化为字节数组,`Deserialize`方法用于从字节数组反序列化出消息。`CalculateCRC32`方法用于计算CRC32校验码。 `JT809Serializer`类是一个辅助类,用于将消息序列化为字节数组和从字节数组反序列化出消息。它使用`JT809Message`类的`Serialize`和`Deserialize`方法实现这些功能,并在序列化时计算CRC32校验码,反序列化时验证CRC32校验码是否正确。 请注意,这只是一个简单的示例代码,实际使用中可能需要根据具体的需求进行修改和扩展。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值