在 C 语言中。 signed char 类型的范围为 -128~127,每本教科书上也这么写。可是没有哪一本书上(包含老师)也不会给你为什么是 -128~127,这个问题貌似看起来也非常easyeasy。 以至于不用去思考为什么,不是有一个整型范围的公式吗:  -2^(n-1)~2^(n-1)-1 (n为整型的内存占用位数)。所以
int 类型 32 位那么就是 -(2^31) ~ 2^31-1 即 -2147483648~2147483647,可是为什么最小负数绝对值总比最大正数多 1 。这个问题甚至有的工作几年的程序猿都模棱两可。由于没有深入思考过,仅仅知道书上这么写。

于是。我不得不深入思考一下这个被很多人忽视的问题。



对于无符号整数,非常easy,所有位都表示数值,比方 char 型,8位。用二进制表示为 0000 0000 ~ 1111 1111,1111 1111 最大即为十进制255。所以 unsigned char 的范围为 0~ 255,在这里普及一下 2 进制转十进制的方法。 二进制每一位的数值乘以它的位权(2^(n-1),n为自右向左的位),再相加,可得到十进制数。比方 :1111 1111 = 1*2^7 + 1*2^6 + 1*2^5 + 1*2^4 + 1*2^3 + 1*2^2 + 1*2^1 + 1*2^0
= 255 。

可是对于有符号整数,二进制的最高位表示正负,不表示数值。最高位为 0 时表示正数,为 1 时表示负数。这样一来,能表示数值的就剩下( n-1 )位了,比方 char a = -1;   那么二进制表示就为
1 0000001。1 表示为 0 0000001,所以 signed char 型除去符号位剩下的 7 位最大为 1111 111 = 127,再把符号加上,0 1111111 = 127,1 1111111 = -127。范围应该为 -127~127 。同理 int 类型也一样,可是问题出来了,教科书上是 -128~127 啊,以下就剖析一下这个惊人的奇葩。

再普及一下计算机内部整数存储形式。大家都知道计算机内部是以二进制来存贮数值的,无符号整数会用所有为来存储,有符号的整数。最高位当做符号位
,其余为表示数值

这样貌似合理, 却带来一个麻烦。当进行加法时,1+1 :

       0000 0001

+     0000 0001

—————————

       0000  0010    ……………… 2 



当相减时 1-1=?

因为计算机仅仅会加法不会减法,它会转化为 1 + (-1) ,因此:

      0000 0001

+    1000 0001

____________________

          1000 0010     …………… -2    



1-1 = -2?

这显然是不正确了,所以为了避免减法运算错误。计算机大神们发明出了反码,直接用最高位表示符号位的叫做原码, 上面提到的二进制都是原码形式,反码是原码除最高位其余位取反,规定:正数的反码和原码同样,负数的反码是原码除了符号位,其余为都取反,因此
-1 的源代码为 1 0000001 ,反码为 1 1111110。



如今再用反码来计算 1+(-1) :

      0000 0001

+    1111 1110

————————

      1111 1111       ………… 再转化为原码就是 1000 0000 = -0  



尽管反码攻克了相减的问题,却又带来一个问题:-0 。既然 0000 0000 表示 0。那么就没有 -0 的必要, 出现 +0 = -0 = 0 ,一个 0 就够了。为了避免两个 0 的问题。计算机大师们又发明了补码。补码规定:
整数的补码是其本身。负数的补码为其反码加一 。所以。负数转化为反码需两个步骤, 第一,先转化为反码,第二: 把反码加一。



这样 -1 的补码为 1111 1111 ,1+(-1) :

     0000 0001

+   1111 1111

________________

  1  0000 0000  ……………………  因为 char 为 8 位。最高位 1 被丢弃结果为 0 ,运算正确。

 

-0 :原码 1000 0000 的补码为 1 0000 0000 。因为 char 是 八位 ,所以取低八位 00000000。   

+0 :原码 0000 0000 ,补码为也为 0000 0000 ,尽管补码 0 都是同样的,可是有两个 0 。既然有两个 0 ,况且 0 既不是正数,也不是负数, 用原码为 0000 0000 表示即可了。



这样一来,有符号的 char,原码都用来表示 -127~127 之间的数了。只有剩下原码 1000 0000 没实用。用排列组合也能够算出来,0 ???????,能表示 2^7 = 128 个数,刚好是 0~127。1 ???????。也能表示
128 个数,总共 signed char 有 256 个数,这与 -127~127 中间是两个 0 刚好吻合。

如今再来探讨一下关于剩下的那个 1000 0000。既然 -127 ~ 0 ~ 127 都有相应的原码与其相应,那么 1000 0000 表示什么呢,当然是 -128 了,为什么是 -128 呢。网上有人说 -0 即 1000 0000 与 128 的补码同样。所以用 1000 0000 表示 -128,,这我实在是不敢苟同。或者说
-128 没有原码。仅仅有补码 1000 0000,胡扯,既然没有原码何来补码,还有说 -128 的原码与 -0(1000 0000) 的原码同样,所以能够用 1000 0000 表示 -128,我仅仅能说。回答得不要那么牵强, 原码 1000 0000 与 -128 的原码实际上是不同的。

但为什么能用它表示 -128 进行运算。假设不要限制为 char 型(即不要限定是 8 位)。再来看,-128 的原码:1 1000 0000 ,9位,最高位符号位。再算它的反码:1 0111 1111。进而。补码为:1
1000 0000,这是 -128 的补码
,发现和原码一样,1 1000 0000 和 1000 0000 同样?假设说一样的人真是瞎了眼了,所以,-128 的原码和 -0(1000 000) 的原码是不同的,可是在 char 型中,是能够用 1000 000 表示 -128 的,关键在于char 是 8 位,它把 -128 的最高位符号位
1 丢弃了
,截断后 -128 的原码为 1000 000 和 -0 的原码同样。也就是说 1000 0000  和 -128 丢弃最高位后余下的 8 位同样,所以才干够用 -0 表示 -128,这样。当初剩余的 -0(1000 0000)。被拿来表示截断后的
-128,由于即使截断后的 -128 和 char 型范围的其它 (-127~127) 运算也不会影响结果。 所以才敢这么表示 -128。

比方 -128+(-1) :

     1000 0000  ------------------丢弃最高位的-128

+     1111  1111   -----------------   -1

________________

   10111  1111    ------------------char 取八位,这样结果不对,只是没关系 ,结果-129本来就超出char型了,当然不能表示了。

比方 -128+127:

     1000 0000

  + 0111 1111

————————

     1111 1111 --------------  -1 结果正确, 所以,这就是为什么能用 1000 0000 表示-128的原因。

从而也是为什么 char 是 -128~127。而不是 -127~127 。short int 相同如此 -32768~32767  由于在 16 位中,-32768 为原码为 17 位,丢弃最高位剩下的 16 为 - 0 的原码相同。



另一个问题,既然 -128 最高位丢弃了。那么为什么还能打印出 -128 ?

//在内存中以补码1 1000 0000 存储,但因为是 char。所以仅仅存储 1000 0000
char a= -128; //既然最高位丢弃了。输出时应该是 1000 000 的原码的十进制数-0,但为什么能输出-128呢。 printf("%d",a);

我猜想是计算机内部的一个约定。就像 float 一样 ,能用 23 位表示 24 位的精度 。由于最高位默觉得 1,到时候把 23 位取出再加 1 便可。

-128 也是相同的原理,当数据总线从内存中取出的是 1000 000 。CPU 会给它再添最高一位,变为 1 1000 0000 这样才干转化为 -128 输出,不然 1000 0000 怎样输出?这当然是我的一种判断,详细怎么实现还得问 CPU 的设计者了。

再看一个样例:

char a=-129;
printf("%d",a); //会输入多少?? 结果为 127 ,为什么呢?

-129 在补码为 10 0111 1111 仅仅取后八位存储,即 0111 111 这个值刚好是 127 了。同理 -130 截断后为 126.....

如此按模轮回,关于模就先不探讨了。

那么:

unsigned  char a=  -1;
if( 1 > a){
printf("大于");
}else{
printf("小于");
}

结果是什么呢?  出人意料的是:小于。而不是大于。猫腻在你哪呢,还是存储问题:

a 为 unsigned 无符号。 它的八位都用来存储数值, 没有符号位,编译器把 -1 转换为补码为 1111 1111。但因为是无符号。计算机会把 1111 11111 当做是无符号来对待。自然就是 2^8 -1  = 255 了,所以相当于是 if( 1 > 255) 肯定是 printf("小于"); 了。

本文转自:http://blog.csdn.net/daiyutage

浅析为什么 char 类型的范围是 : -128~+127的更多相关文章

  1. 浅析为什么char类型的范围是 —128~+127

    转载于daiyutage 在C语言中, signed char 类型的范围为-128~127,每本教科书上也这么写,但是没有哪一本书上(包括老师)也不会给你为什么是-128~127,这个问题貌似看起来 ...

  2. [转载]浅析为什么char类型的范围是 —128~+127

    http://blog.csdn.net/daiyutage/article/details/8575248 在C语言中, signed char 类型的范围为-128~127,每本教科书上也这么写, ...

  3. 解读为什么有符号的char可表示范围是-128~+127

    问:为什么有符号的char可表示范围是-128~+127? 要明白这个问题,首先要明白一下几点: 对于char和int计算机中以补码形式存在. 严格来说计算机就是傻逼,它只知道某个位上是0还是1. 我 ...

  4. C语言 1字节signed char的范围为什么是-128~127?

    参考 1. 关于 -128 ,+128,-0,+0,-1 的反码补码 | 博客园 2. 八位二进制数为什么表示范围(-128~~+127)理解 | 博客园 无符号单字节范围 无符号单字节unsigne ...

  5. 转:浅谈char类型范围

    在 C 语言中, signed char 类型的范围为 -128~127,每本教科书上也这么写,但是没有哪一本书上(包括老师)也不会给你为什么是 -128~127,这个问题貌似看起来也很简单容易, 以 ...

  6. 带符号的char类型取值范围为什么是-128——127

    以前经常看到带符号的char类型取值范围是-128——127,今天突然想为什么不是-127——127,-128是怎么来的? 127好理解,char类型是8位,最高位是符号位,0正1负,所以011111 ...

  7. C语言基础(6)-char类型

    1. char常量.变量 使用单引号‘’引起来的就是char的常量 ‘a’是一个char类型的常量 “a”是一个字符串类型的常量 1是一个int型的常量 ‘1’是一个char型的常量 char a; ...

  8. 在vs中char类型的实参与LPCWSTR类型的形参类型不兼容怎么解决?

    今天在做 COS脚本解释器的时候,遇到了这个问题 先了解一下 LPCTCHAR 这个东东 LPCTSTR用来表示你的字符是否使用UNICODE, 如果你的程序定义了UNICODE或者其他相关的宏,那么 ...

  9. unsigned char 类型

    在蓝牙4.0的开发中,很多数据类型都用到了 unsigned char ,我觉得用这个类型的一个原因是相比较于整型,它占的空间更少. 比如: unsigned char a = 1;  // 占1个字 ...

随机推荐

  1. -bash: wget 未找到命令的解决办法

    在Linux操作系统中,我们会经常要用到wget下载文件.wget非常稳定,它在带宽很窄的情况下和不稳定网络中有很强的适应性. 在linux中使用wget时,若报-bash: wget: comman ...

  2. Spring boot 使用@Value注入属性

    Spring boot 使用@Value注入属性 学习了:http://blog.csdn.net/hry2015/article/details/72353994 如果启动的时候报错: spring ...

  3. H2数据库入门使用

    H2数据库入门使用 学习了: https://www.cnblogs.com/xdp-gacl/p/4171024.html http://www.cnblogs.com/xdp-gacl/p/417 ...

  4. 协议栈处理中的conntrack HASH查找/Bloom过滤/CACHE查找/大包与小包/分层处理风格

    1.路由CACHE的优势与劣势 分级存储体系已经存在好多年了.其精髓在于"将最快的存储器最小化.将最慢的存储器最大化",这样的结果就使资源利用率的最大化.既提高了訪问效率,又节省了 ...

  5. linux内核模块笔记

    主题: 1. 嵌入式基础知识 2. linux内核介绍 3. 内核的编译和安装(x86) 4. 第一个模块 5. 模块的相关工具 6. 模块的符号导出 7. 模块的參数 1.看linux/module ...

  6. Leetcode_num1_Single Number

    好久没有做题啦.从今天開始刷Leetcode的题.希望坚持的时间能长一点. 先从ac率最高的Single Number開始吧. 题目: Given an array of integers, ever ...

  7. po层和vo层中po和vo是什么意思

    POJO(Plain Ordinary Java Object)简单的Java对象,实际就是普通JavaBeans,是为了避免和EJB混淆所创造的简称. pojo 是Plain Old Java Ob ...

  8. sqlplus登录提示:ORA-12162:TNS:net service name is incorrectly specified错误

    [root@localhost ~]# su - oracle [oracle@localhost ~]$ sqlplus '/as sysdba' SQL*Plus: Release 11.2.0. ...

  9. 使用C++实现学生管理系统

    我在前面的博客中分别使用C语言的动态数组和链表实现了学生成绩管理系统.近期正好在学习C++,于是我便使用C++实现了学生成绩管理系统.算法和前面的C语言的动态数组实现的学生成绩管理系统几乎相同,仅仅是 ...

  10. hdu 4628 Pieces(状态压缩+记忆化搜索)

    Pieces Time Limit: 6000/3000 MS (Java/Others)    Memory Limit: 131072/131072 K (Java/Others) Total S ...