c/c++ int 范围的原因
在C语言中, signed char 类型的范围为-128~127,每本教科书上也这么写,但是没有哪一本书上(包括老师)也不会给你为什么是-128~127,这个问题貌似看起来也很简单容易, 以至于不用去思考为什么,不是有一个整型范围的公式吗: -2^(n-1)~2^(n-1)-1 n为整型的内存占用位数,所以int类型32位 那么就是 -(2^31)~2^31 -1 即
-2147483648~2147483647,但是为什么最小负数绝对值总比最大正数多1 ,这个问题甚至有的工作几年的程序员都模棱两可,因为没有深入思考过,只知道书上这么写。。于是,我不得不深入思考一下这个被许多人忽视的问题。。
对于无符号整数,很简单,全部位都表示数值,比如 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=127 。
但是对于有符号整数,二进制的最高位表示正负,不表示数值,最高位为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 …………………… 这里变成了9位,由于char 为8位,最高位1 被丢弃 结果为0 ,运算正确
再看, -0 :原码 1000 0000 的补码为1 0000 0000 ,由于char 是 八位 ,所以取低八位00000000, +0 :原码为0000 00000 ,补码为也为 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最高位丢弃了。那么
char a=-128; //在内存中以补码1 1000 0000 存储,但由于是char ,所以只存储 1000 0000
printf("%d",a); //既然最高位丢弃了,输出时应该是1000 000 的原码的十进制数-0 ,但为什么能输出-128呢。
还能打印出-128;
我猜想是计算机内部的一个约定,就像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("小于");了。。。
。。。。。。。。。。。
好了,就说到这儿吧。。。。。。。。
---------------------
作者:若水三千你是一千
来源:CSDN
原文:https://blog.csdn.net/daiyutage/article/details/8575248
版权声明:本文为博主原创文章,转载请附上博文链接!
c/c++ int 范围的原因的更多相关文章
- mysql存储之int
开始之前给大家出个问题,数据库表test中两个字段 a int(2),b int(3),现在想执行下面的插入语句 ,) 思考是否可以插入? 答案是能插入 再看下面的语句 ,) 思考能不能插入?注意第 ...
- input()报错:TypeError: '>=' not supported between instances of 'str' and 'int'
今天学习python基础—分支与循环,接触到了if.在练习一个if语句的时候,出现了错误. 题目是: 根据分数划分成四个级别:优秀.良好.及格.不及格,分数是72: grade = 72if grad ...
- int **指针问题
转自:http://blog.csdn.net/u012501459/article/details/45395571 在打印二维数组时遇到了问题,二维数组可以这样定义int matrix[ROWS] ...
- C/S架构应用程序开发培训笔记
最近为客户组织了一项C/S架构程序的开发培训,讲解C/S应用程序开发中需要注意的点. 我主要是做C/S方面的ERP/CRM程序开发,界面是用Windows Forms技术,有遗漏或错误的地方欢迎批评指 ...
- HDU1532 网络流:最大流之福德福克森算法
问题描述:约翰是个农民,每次下雨的时候他的庄家总是会被淹没,这就意味着当庄家被水淹后需要很长时间才能重新生长出来,因此,约翰已经建立了一系列排水管道为了使他的庄家尽可能被淹没的最少,也就是说管道的排水 ...
- C/C++文字常量与常变量的概念与区别 分类: C/C++ 2015-06-10 22:56 111人阅读 评论(0) 收藏
以下代码使用平台是Windows 64bits+VS2012. 在C/C++编程时,经常遇到以下几个概念:常量.文字常量.符号常量.字面常量.常变量.字符串常量和字符常量,网上博客资料也是千篇千律,不 ...
- php易混淆知识点
一.define(“constant”, “hello world”);和const constant = “hello world”;的区别? (0).使用const使得代码简单易读,const本 ...
- C Primer Plus_第三章_数据和C_复习题与编程练习
Review long代替int类型变量的原因是什么? 在您的系统中,long可以容纳比int更大的数:如果您确实需要处理更大的值,那么使用一种在所有系统上都保证至少是32位的类型会使程序的可移植性更 ...
- shopnc2014年11版数据库字典
shopnc_activity 表注释: 活动表 字段 类型 空 默认 注释 activity_id mediumint(9) 否 id activity_title varchar(255) 否 ...
随机推荐
- centos7运行yum报如下提示:Run "yum repolist all" to see the repos you have
centos7运行yum报如下提示: There are no enabled repos. Run "yum repolist all" to see the repos you ...
- iview+vue 表格中添加图片
开门见山,话不多说,要在表格中添加图片,可以使用td: <table " width="100%"> <tr class="tr-style ...
- python学习那点事---列表生成式实现大小写字母相互转换
题目: 已知列表list=["pYTHON","iS",eASY],要求使用列表生成式实现,生成一个新的列表,要求将大写字母转换为小写字母,小写字母转换为大写字 ...
- poj3468 A Simple Problem with Integers (树状数组做法)
题目传送门 A Simple Problem with Integers Time Limit: 5000MS Memory Limit: 131072K Total Submissions: 1 ...
- XX-net 3.11.9 登陆Google等出现没有开启cookie的问题
糟糕!您的浏览器似乎禁用了 Cookie.请务必启用 Cookie 或尝试打开一个新的浏览器窗口. 出现这个问题解决方法: 1.配置好X-tunnel,即登录账号2.打开谷歌浏览器或者你用的浏览器,设 ...
- pycharm格式化python代码快捷键Ctrl+Alt+L失效
突然发现按Ctr+Alt+L格式化python失效了,下午时候还好好的.看网上得说法是因为开着的其他软件里用了全局快捷键Ctr+Alt+L,我的是因为被网易云音乐占用了,所以在网易云音乐里把这个快捷键 ...
- vue 实现active点击图片切换
循环条件下: 1.点击函数@click="active(index)" 获取点击的位置 2.讲索引值传给class,点击哪一个则显示哪一个的样式 3.在data添加ins的初始值 ...
- SQL数据库—<6>存储过程
Transact-SQL中的存储过程,非常类似于Java语言中的方法,它可以重复调用.当存储过程执行一次后,可以将语句缓存中,这样下次执行的时候直接使用缓存中的语句.这样就可以提高存储过程的性能. Ø ...
- LeetCode Linked List Easy 83. Remove Duplicates from Sorted List
Description Given a sorted linked list, delete all duplicates such that each element appear only onc ...
- ASN.1
ASN.1抽象语法标记(Abstract Syntax Notation One https://baike.baidu.com/item/ASN.1/498523?fr=aladdin