在 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. oracle数据库服务介绍

    共有7个服务,这七个服务的含义分别为: 1. Oracle ORCL VSS Writer Service:Oracle卷映射拷贝写入服务,VSS(Volume Shadow Copy Service ...

  2. 模块打包机--webpack--基础使用

    什么是webpack? 作用有哪些?   WebPack可以看做是模块打包机:它做的事情是,分析你的项目结构,找到JavaScript模块以及其它的一些浏览器不能直接运行的拓展语言(Scss,Type ...

  3. tp框架,addAll方法报错,返回false

    tp框架的批量添加addAll($data)方法很实用,但是注意,数据数组的数据结构要保持一致,不然会返回false.

  4. POJ——T2421 Constructing Roads

    http://poj.org/problem?id=2421 Time Limit: 2000MS   Memory Limit: 65536K Total Submissions: 24132   ...

  5. <pre>标签

     <pre>标签最主要的认识就是预格式化文本,被包围在 pre 元素中的文本一般会保留空格和换行符.而文本也会呈现为等宽字体.经经常使用于在网页中显示计算机源码. 1.格式化文本举例 &l ...

  6. Libvirt中windows虚拟机的动态内存管理

    非常短的前提 Libvirt支持对虚拟机进行内存动态扩展,可是windows虚拟机首先须要安装virtio-win驱动. KVM提供的virtio-win驱动下载地址: http://www.linu ...

  7. 百度分页效果之纯jsp版

    数据库连接工具类 package com.gao.page.utils; import java.sql.Connection; import java.sql.DriverManager; publ ...

  8. ZOJ Problem Set - 3820 Building Fire Stations 【树的直径 + 操作 】

    题目:problemId=5374" target="_blank">ZOJ Problem Set - 3820 Building Fire Stations 题 ...

  9. 省赛i题/求1~n内全部数对(x,y),满足最大公约数是质数的对数

    求1~n内全部数对(x,y),gcd(x,y)=质数,的对数. 思路:用f[n]求出,含n的对数.最后用sum[n]求和. 对于gcd(x,y)=a(设x<=y,a是质数),则必有gcd(x/a ...

  10. (转载)10个实用的但偏执的Java编程技术

    10个实用的但偏执的Java编程技术 在沉浸于编码一段时间以后(比如说我已经投入近20年左右的时间在程序上了),你会渐渐对这些东西习以为常.因为,你知道的…… 作者:小峰来源:码农网|2015-09- ...