挖一挖unsigned int和补码
文章要讨论的是两部分:
1. 原码,反码和补码。
2. short, unsigned short, int, unsigned int, long, unsigned long的表示及转换
1. 原码,反码和补码
原码是最直观的表示方式:最高位表示符号(0表示正,1表示负),其余位表示大小。假设占位为1字节的数,原码表示的范围就是[-127 ~ 127]一共255个数字。理论上8个bit可以表示256个数,我们只能表示255个,是原码的设计让10000000和00000000都可以表示0。[1]
计算机中使用的不是原码,而是补码。这样做的原因在于:为了简化计算,计算机把1-1当作1+(-1)来做,从而只需要设计加法的实现。然而原码的表示无法让1-1和1+(-1)结果相等。反码虽然可以,但是最后的是1-1=+0,1+(-1)=-0,导致了0有两种表示方法。只有补码的设计,让1+(-1)和1-1得到了满意的一致结果(所有位数都为零,我们用它来表示补码的0)。
反码的定义:正数的反码就是其本身;负数的反码表示,是将其除了最高位,其余全部取反。因此,我们依然可以从最高位看出其正负。
补码的定义:正数的补码就是其本身;负数的补码是在其原码的基础上, 符号位不变, 其余各位取反, 最后+1(即在反码的基础上+1),0的补码表示是唯一的,就是所有位全零。
例子:
[+1] = [00000001]原 = [00000001]反 = [00000001]补
[-1] = [10000001]原 = [11111110]反 = [11111111]补
因为机器使用补码, 所以对于编程中常用到的32位int类型, 可以表示范围是: [-231, 231-1] 因为第一位表示的是符号位。而使用补码表示时又可以多保存一个最小值.
归纳起来,有几个注意点:
(1) 相同位数下,原码和反码可以表示的下限相同,补码可以表示的最小值则比他们还要小1。
以8位为例,原码和反码的下限都是-(27-1),原码的表示是11111111,反码的表示是10000000,补码的-(27-1)表示方式是10000001(反码+1),但是补码还可以用10000000表示-27。上限是正数,大家表示方法相同,因此一致。
(2) 补码表示方法中,最小值的表示方法是最高位是1,其余全为0。
2. short, unsigned short, int, unsigned int, long, unsigned long的表示和混用的结果
cpu, OS, complier都可以32位和64位之分。但是决定一种类型占的字节数的,最直接的是complier的位数。(Ultimately the compiler does, but in order for compiled code to play nicely with system libraries, most compilers match the behavior of the compiler[s] used to build the target system.[2])
常用数据类型对应字节数[3]
32位编译器:
char :1个字节
char*(即指针变量): 4个字节(32位的寻址空间是2^32, 即32个bit,也就是4个字节。同理64位编译器)
short int : 2个字节
int: 4个字节
unsigned int : 4个字节
float: 4个字节
double: 8个字节
long: 4个字节
long long: 8个字节
unsigned long: 4个字节
64位编译器:
char :1个字节
char*(即指针变量): 8个字节
short int : 2个字节
int: 4个字节
unsigned int : 4个字节
float: 4个字节
double: 8个字节
long: 8个字节
long long: 8个字节
unsigned long: 8个字节
跨平台时为了避免问题,往往使用__int8, __int16,__int32,__int64。
混用的结果
比如出现:unsigned int a = 3; return a * -1; 结果会如何呢?
首先,不同类型的数在一起运算,必然会让编译器将它们划为同一类型再进行计算。这种类型间的自动转化标准,被称作Usual arithmetic conversions。下面是摘自MSDN上关于它的说明[4]:
If either operand is of type long double, the other operand is converted to type long double.
If the above condition is not met and either operand is of type double, the other operand is converted to type double.
If the above two conditions are not met and either operand is of type float, the other operand is converted to type float.
If the above three conditions are not met (none of the operands are of floating types), then integral conversions are performed on the operands as follows:
If either operand is of type unsigned long, the other operand is converted to type unsigned long.
If the above condition is not met and either operand is of type long and the other of type unsigned int, both operands are converted to type unsigned long.
If the above two conditions are not met, and either operand is of type long, the other operand is converted to type long.
If the above three conditions are not met, and either operand is of type unsigned int, the other operand is converted to type unsigned int.
If none of the above conditions are met, both operands are converted to type int.
这样,这个问题就好回答了,-1会被默认为int型,但是int和unsigned int做运算,int会被自动转化为unsigned int。
那么-1转换为unsigned int会是什么?
有了第一节中的讨论,下面的推论就非常明显:计算机中的表示方法是补码,int的字节数是4字节,因此-1在机器中是:0xFFFFFFFF。
这个时候我们将它当作unsigned int识别出来,unsigned int的特点是:最高位不作为符号位,所有位都表示值。
因此32位编译器上,unsigned int的范围是[0, 232-1],int的范围是[-231, 231-1](补码可以多表示一个最小值)
当0xFFFFFFFF的所有位都作为数值位时,其十进制表示就成了232-1,再乘以3,毫无疑问超过了32位而出现溢出,unsigned int取前32位,结果就是0xfffffffd,一个接近unsigned int上限的正整数。
这道例题来自http://blog.sina.com.cn/s/blog_4c7fa77b01000a3m.html,据说是微软面试题 :)
在上题的分析中,我们也可以发现一点:
机器中的补码总是不会变的,当我们把它们定义为不同的类型(int, unsigned)编译器将他们解读出来的值就会不同。
举个例子,例子来自[5]的节选:
unsigned b = -10;
if (b) printf("yes\n"); else printf("no\n");
int c = b;
printf("%d\n", c);
unsigned a = 10;
int d = -20;
int e = a + d;
printf("%d\n", e);
答案是:
yes
-10
-10
原因正如上面所说,传值传的是机器中的补码,总不会变(溢出除外),unsinged int和int只是定义了编译器解读它们的方式。
[5] 中还有一道题也非常有意思,这里就不转过来了,各位看官有兴趣可以移步去看看 :)
参考文章:
http://www.cnblogs.com/zhangziqiu/archive/2011/03/30/ComputerCode.html (这篇文章写的是真好,深入浅出级别。第一部分基本上来自这篇博文)
http://stackoverflow.com/questions/13764892/what-determines-the-size-of-integer-in-c
http://www.cnblogs.com/augellis/archive/2009/09/29/1576501.html
http://msdn.microsoft.com/en-us/library/3t4w2bkb.aspx
http://www.cnblogs.com/krythur/archive/2012/10/29/2744398.html
挖一挖unsigned int和补码的更多相关文章
- 深度解析C语言int与unsigned int
就如同int a:一样,int 也能被其它的修饰符修饰.除void类型外,基本数据类型之前都可以加各种类型修饰符,类型修饰符有如下四种:1.signed----有符号,可修饰char.int.Int是 ...
- 关于unsigned int和int的加法
补码(two's complement) 在计算机系统中,数值一律用补码来表示和存储.原因在于,使用补码,可以将符号位和数值域统一处理:同时,加法和减法也可以统一处理.此外,补码与原码相互转换,其运算 ...
- 深入解剖unsigned int 和 int
就如同int a:一样,int 也能被其它的修饰符修饰.除void类型外,基本数据类型之前都可以加各种类型修饰符,类型修饰符有如下四种: 1.signed----有符号,可修饰char.int.Int ...
- 挖一挖@Bean这个东西
有Bean得治 任何一个正常程序的访问都会在内存中创建非常多的对象,对象与对象之间还会出现很多依赖关系(一个处理业务逻辑的类中几乎都会使用到别的类的实例),一般的做法都是使用new关键字来创建对象,对 ...
- unsigned int与int相加的问题-----C/C++小知识 区别
http://blog.csdn.net/thefutureisour/article/details/8147277 #include "stdafx.h" int _tmain ...
- -1>1?! unsigned int的世界不简单
编程语言提供了很多的基本数据类型,比如char,int,float,double等等.在C和C++的世界中,还有一种类型,叫做无符号数据,修饰符位unsigned,比如今天要说的unsigned in ...
- unsigned int 和 int
就如同int a:一样,int 也能被其它的修饰符修饰.除void类型外,基本数据类型之前都可以加各种类型修饰符,类型修饰符有如下四种:1.signed----有符号,可修饰char.int.Int是 ...
- 坑!坑!坑!防不胜防的unsigned int的运算
我很早之前就知道,unsigned int与int运算的时候,int会被转化为unsigned int来进行运算.一直觉得定这条规则的人是极度反人类的,虽说unsigned int可以表示更大的正值, ...
- 挖一挖C#中那些我们不常用的东西之系列(3)——StackTrace,Trim
时间太快了,三月又要过去了,告别一下...继续期待生死未卜的四月,今天我们继续挖一挖. 一: Environment.StackTrace 可能我们看到最多的就是catch中的e参数,里面会有一个St ...
随机推荐
- 2018java开发一些面经
算法系列:https://www.cnblogs.com/yanmk/p/9232908.html 2018Java开发面经(持续更新) 不要给自己挖坑!!!不要给自己挖坑!!!不要给自己挖坑!!!如 ...
- 冲刺ing-1
冲刺一 1.第一天的工作分配: 姓名 任务分工 吴伟华(队长) 布置团队任务,发表汇总博客及第一次冲刺博客 蔺皓雯 讨论任务分配 杨池宇 讨论任务分配 鲁婧楠 讨论任务分配 曾茜 讨论任务分配 蔡晨旸 ...
- 20172333 2017-2018-2 《Java程序设计》第7周学习总结
20172333 2017-2018-2 <Java程序设计>第7周学习总结 教材学习内容 1.继承是创建新类的快捷方式之一,继承可以使用父类的所有方法及对象. 2.继承具有单向性,父类不 ...
- Mininet实验 多个数据中心的拓扑网络实现
实验目的 掌握多数据中心网络拓扑的构建 掌握多数据中心数据交换过程 实验原理 主机间发送消息上报给交换机,交换机对收到的报文信息进行分析判断,如果交换机中存在此消息相对应的流表,则交换机直接下发流表, ...
- (转)Android SearchView 搜索框
如果对这个效果感觉不错, 请往下看. 背景: 天气预报app, 本地数据库存储70个大中城市的基本信息, 根据用户输入的或通过搜索框选取的城市, 点击查询按钮后, 异步请求国家气象局数据, 得到返回的 ...
- IT启示录
引用电影<夏洛特烦恼>中夏洛的一句话:"一直以来,我根本就不知道自己想要什么".可以说在写这篇博客之前我仍然没有考虑清楚之后的道路,即使早已明确了走游戏开发的道理,却不 ...
- Swift as as!和as?的区别
1.as的使用场合 1.从派生类转换为基类,向上转类型(upcasting) class Animal{} class Dog:Animal{} let cat = ANimal() let dog ...
- Jenkins系列-Jenkins升级、迁移和备份
升级Jenkins Jenkins的开发迭代非常快,每周发布一个开发版本,长期支持版每半年更新一次(ps:大版本更新).如此频繁的更新,怎么升级呢? war:下载新版的war文件,替换旧版本war文件 ...
- 《Effective C#》快速笔记(二)- .NET 资源托管
简介 续 <Effective C#>读书笔记(一)- C# 语言习惯. .NET 中,GC 会帮助我们管理内存,我们并不需要去担心内存泄漏,资源分配和指针初始化等问题.不过,它也并非万能 ...
- C的强制转换和C++的强制转换(转)
C的强制转换: (type)<expression> 其中,type为类型描述符,如int,float等.<expression>为表达式.经强制类型转换运算符运算后,返回一个 ...