在 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. STM32 IIC双机通信—— HAL库硬件IIC版

    参考传送门 关于IIC的原理这里我就不多说了,网上有很多很好的解析,如果要看我个人对IIC的理解的话,可以点击查看,这里主要讲一下怎样利用STM32CubeMx实现IIC的通讯,经过个人实践,感觉HA ...

  2. JS中的DOM操作怎样添加、移除、移动、复制、创建和查找节点

    DOM操作怎样添加.移除.移动.复制.创建和查找节点? (1)创建新节点 createDocumentFragment() //创建一个DOM片段 createElement() //创建一个具体的元 ...

  3. [Angular] Fetch non-JSON data by specifying HttpClient responseType in Angular

    By default the new Angular Http client (introduced in v4.3.1) uses JSON as the data format for commu ...

  4. Boost Log 基本使用方法

    Boost Log 基本使用方法 flyfish 2014-11-5 依据boost提供的代码演示样例,学习Boost Log 的基本使用方法 前提 boost版本号boost_1_56_0 演示样例 ...

  5. Maven传递依赖的时候,同名包不同版本号的包均会下载,可是编译的时候,仅仅会载入一个高版本号的。

    描写叙述,在一个Maven项目中.同一时候依赖了spring-tomcat-weaver  和  struts-core 包.可是spring-tomcat-weaver 须要commons-dige ...

  6. bzoj5204: [CodePlus 2018 3 月赛]投票统计(离散化+暴力)

    5204: [CodePlus 2018 3 月赛]投票统计 题目:传送门 题解: 谢谢niang老师的一道sui题 离散化之后直接搞啊(打完之后还错了...) 代码: #include<cst ...

  7. JavaScript学习记录三

    title: JavaScript学习记录三 toc: true date: 2018-09-14 23:51:22 --<JavaScript高级程序设计(第2版)>学习笔记 要多查阅M ...

  8. 修改host文件浏览国外网站

    公司电脑网络没法进github没办法工作需要只能FQ了. 方法1:用VPN 但是地要钱呐,没钱只能放弃了,不过每天试用还是可以的 方法2:改电脑host,文件中每条数据前面的#代表注释.把要访问的地址 ...

  9. 忽略PyCharm4中特定的警告提示信息

    有一种简单的方式忽略PyCharm4中的警告,光标放在下划线上,a. 等灯泡出现,点击灯泡或者b. 按下Alt和Enter键,选择'Ignore errors like this'. 或者,在设置里更 ...

  10. RGB颜色值与十六进制颜色码转换工具

    RGB颜色值转换成十六进制颜色码:      十六进制颜色码转换成RGB颜色值:     颜色码对照表 颜色 英文代码 形象描述 十六进制 RGB LightPink 浅粉红 #FFB6C1 255, ...