C语言打印16进制出现0xffffff现象的问题剖析!
2015-09-07 21:57
525 查看
今天在博问里面看到一个朋友的问题,大致是在网络程序中,打印出来的16进制数,莫名的出现ffffff。例如,某个byte真是值为0xc9,打印出来确是0xffffffc9。原博问连接如下:http://q.cnblogs.com/q/71073/
其实类似的问题不是只在网络程序中才会出现的,看示例代码:
程序输出如下:
可以看到:
把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 #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)。
因此打印正常。
以上分析,如有不正确的地方请各位指正。
相关文章推荐
- C++ 大数处理
- Python使用C语言生成的库
- c++之模板
- 项目4.4计算圆柱体表面积
- cmake 编译opencv 出现 The C++ compiler "C:/Program Files/Microsoft Visual Studio 10.0/VC/bin/cl.exe
- 练习3.42 将含有整数元素的vector对象拷贝到数组上
- C++ Vector swap操作前后迭代器为何不失效
- C++Primer快速浏览笔记-复合类型
- C++11在时空性能方面的改进
- C语言篇——小谈预处理命令
- C语言利用栈实现将中缀表达式转换为后缀表达式(即逆波兰式)
- 【C语言】-变量之间的类型转换
- C++递归生成格雷码
- C++技术点积累(2)——拷贝构造函数、深拷贝、浅拷贝
- HDU 1542 Atlantis (线段树 +离散化+ 扫描线)
- 第一课:C语言编程
- C++ 之 explicit,mutable,volatile 浅析
- Day-1:C语言基本知识:
- C++ 墙角知识 03
- 标准C++中的string类的用法总结