c的基础 1. 无符号数和补码
计算机中储存和处理的信息是以二进制信号表示的。单个的位不是是很实用,而将这些位 组合在一起,加上某种解释,即给不同的可能位模式赋予含义,我们就行表示怎样有限集合的元素,即实现各种数据结构。计算机中使用8位的块称之为字节作为最小的可寻址的存储器单位,机器级程序将存储器视为一个很大的字节数组,称为虚拟存储器。存储器的每一个字节都有一个唯一的数字来标识,称为地址,全部可能地址的集合称为虚拟地址空间。
c语言中的指针,指针的值为某个存储块的第一个字节的虚拟地址。C编译器将每一个指针和类型信息结合起来,依据指针值的类型也就是声明的指针类型,来生成不同的机器级代码来訪问储存在指针所指向位置出的值每一个程序对象能够简单的视为一个字节块,而程序本身就是一个字节序列。
字长,32位系统为4个字节,64位系统为8个字节,对于32位字长时,其能表示的虚拟地址范围为0 - 2^32-1 ,即虚拟地址空间最大为4GB。
大端法:最高有效字节在低地址位。
小端法:最高有效字节在高地址位。
一般系统都使用的是大端法。
C中主要的位运算:
^异或, 1 ^ 0 = 1 , 0 ^ 1 = 1, 0 ^ 0 = 0, 1 ^ 1 = 1 .
& 且 |或 ~取反
<< n 左移 n 位 >> n 右移n位
c中的右移有两种,逻辑右移和算术右移,逻辑右移即在左端补0,而算术右移在左端不最高有效位的值。一般对于有符号数进行的是算术右移,
java中使用>>表示算术右移,而使用>>>表示逻辑右移。
位运算操作符的优先级都低于 加减 号, 所以 在算式中 一般都要用括号 括起来。
对位运算的一个常见使用方法是实现掩码运算。掩码就是指从字节中选出一定位的集合。
计算机系统中有三种重要的数字表示,无符号(unsigned)编码,仅仅能表示非负的数,补码(two's-complement)编码,用来表示有符号整数,浮点数(float-point)编码,表示实数的科学计数发的二进制版本号。仅仅有C系语言有无符号数这个概念,但我觉得无符号数的存在是一种长处。
这里讨论两种编码,无符号和补码。
无符号数的编码即直接将二进制数据转换为10进制,就能够得到结果。其最大值为 2 ^ w - 1,w为其位数,最小值为0.
补码:最高有效位解释或为负权,即 - 2 ^w ,然后将全部位乘以权值得到结果。最高有效位即为符号位。最大值为 2 ^(w-1)-1,最小值为 - 2^w.补码的范围是不正确称的。
因为不同机器上的数据类型可能有不同的取值范围,如long,在32位系统中为4个字节,但在64位系统中为8个字节。所以一般使用在stdint.h中定义的数据类型intN_t和unintN_t,来声明N位的有符号数和无符号数,则会使程序在不同系统中都能够正确执行。N一般去8,16,32,64。
有符号数和无符号数之间的转换:
程序读取不同类型时,仅仅是对存储器上的信息以不同的方式进行解释,而C语言进行强制转换时,也仅仅是改变了其解释存储器上位信息的方式,并没有改变存储器上的信息本身。
所以相同位长的无符号数和有符号数直接的转换,不改变底层的位信息,仅仅是换了一种解释的方法来解释这些位。如对无符号的int 4 294 967 295转换为有符号的int就是 -1,而有符号的int 的-1转换为无符号的int 就是4 294 967 295。
当一个运算中 一个运算数为有符号,一个运算数为无符号数,C语言会隐式地将有符号数强制类型转换为无符号数,,然后运行两个无符号数之间的运算。对于标准的运算来说,这样做不会出错。如 c = a + b, 当中c和a为有符号int,a为-1,b为无符号int为1,则运算时,将a转换为无符号数,也就是 4 294 967 295,然后加b,应得到的是4 294 967 296,但在位级上,因为最高位的1超过范围而溢出,仅仅剩下32个0,则最后c的结果为0。能够看出,这里对普通的运算没有影响。
但在一些关系表达式中,则会出错。如 -1 < 0u。u表示无符号数,这里将-1转换为无符号的int后,值为4 294 967 295,肯定是比0要大的,然后就出错了。
在不同字长的整数之间进行转换,须要进行为的扩展和截断。
扩展:
无符号数扩展使用,零扩展:在表示的开头加入0。
有符号数扩展使用,符号扩展:在表示中加入最高有效位的值的副本。即符号位为1时,加入若干个1。
有符号的负数扩展时,前面加入1不会对最后的数值有影响,假定扩展前为w位,扩展后为v位,扩展前的值为-i,则有扩展前除去最高位,剩下为表示的整数为j = 2^w - i,则扩展后,从符号位到原来符号位,即除去j表示数据的部分的剩余部分表示的值为: - 2 ^(v-1) + 2^(v-2)+...+2^w ,则扩展后表示的值为 - 2 ^(v-1) + 2^(v-2)+...+2^w + 2 ^w-- i.则前面的数都能够消去,即得到最后结果为-i。
.C中同一时候进行有无符号转换和不同字长的数据转换时,想转换不同字长的数据类型,即先进行扩展或者截取,然后在使用有符号或者符号的方式来解释数据。
截断:
截取时即舍去高n位就可以。
整数运算:
无符号数的加法,会导致结果大于数据类型的范围,则无符号的运算事实上是一种模运算,利用溢出机制,实现计算和模2^w。
算术运算溢出,指完整的整数结果不能放到数据类型的字长限制中去。推断两个数相加,如 c = a + b,是否发生溢出,仅仅要推断 c是否 小于a或者b就可以,由于a或者b肯定是小于2^w。
补码的加法:
两个有符号数的w位补码之和与无符号数之和有全然相同的位级表示,计算机使用相同的机器指令来运行无符号或有符号的加法。如之前进行的分析: c = a + b, 当中c和a为有符号int,a为-1,b为无符号int为1,则运算时,将a转换为无符号数,也就是 4 294 967 295,然后加b,应得到的是4 294 967 296,但在位级上,因为最高位的1超过范围而溢出,仅仅剩下32个0,则最后c的结果为0。
假设将这里的b也看作是有符号数,其结果依旧如此且正确。
所以,我们能够理解为,补码的加法是 先将其转换为无符号数,然后相加,得到的结果在转换为有符号数。
这里有溢出,正溢出和负溢出。
正溢出:得到的结果应该位于2^(w-1) —— 2 ^w - 1,但使用补码转换为有符号数后是在 0 —— - 2 ^ (w-1)这个范围内。
负溢出:得到的结果应该在-2^w —— -2^(w-1),可是因为溢出,结果在 0 —— 2^(w-1) -1上。
其它情况为正常。
补码的非:
即取负。对于 -2 ^(w-1)取非为 其本身。对于其它的值能够得到正确结果。
无符号的乘法:
即计算乘积后模 2^w。
补码的乘法:
补码乘积的范围在 -2^(2w-2) + 2^(w-1) 和 -2^(2w-2)之间。这里,对于无符号和补码的乘法运算,其位级表示依旧是一样的,所以对于补码的乘法是将其转换为无符号数,然后相乘,结果模2^w。这里对于运算的分析:
令x,y的无符号值为 a,b,其符号位值为 c,d。则 a = x + c * 2^w , b = y + d * 2 ^ w;
a * b % 2^w
= ( (x + c * 2^w )*( y + d * 2 ^ w) )%2^w
= (x * y + (y *c + x*d + c*d * 2^w)* 2^w)% 2^w
= (x*y)% 2^w
计算机上的乘法指令相当慢,一般须要10个以上的时钟周期。编译器会使用一项重要的优化,用移位和加法运算和减法运算来取代乘以常数的乘法。如使用(x<<4)-(x<<1)来取代x*14。
计算机上的除法的速度就更加慢了,一般须要30个以上的时钟周期。除以2的幂能够通过右移操作进行,对于无符号是逻辑移位,对于补码进行算术移位。
c的基础 1. 无符号数和补码的更多相关文章
- C语言基础(5)-有符号数、无符号数、printf、大小端对齐
1.有符号数和无符号数 有符号数就是最高位为符号位,0代表正数,1代表负数 无符号数最高位不是符号位,而就是数的一部分而已. 1011 1111 0000 1111 1111 0000 1011 10 ...
- C++有符号和无符号数的转换
本文转自:http://www.94cto.com/index/Article/content/id/59973.html 1.引例: 今天在做了一道关于有符号数和无符号数相互转换及其左移/右移的问题 ...
- C语言基础 (4) 原码反码补码与数据类型
1.回顾 使用gcc编译代码 gcc hello.c -o hello windows下编译代码 C语言编译步骤: 预处理(头文件展开,干掉注释) gcc -E hello.c -o hello.i ...
- 关于有符号数和无符号数的转换 - C/C++
转载自:http://www.94cto.com/index/Article/content/id/59973.html 1.引例: 今天在做了一道关于有符号数和无符号数相互转换及其左移/右移的问题, ...
- C语言基础(4)-原码,反码,补码及sizeof关键字
1. 原码 +7的原码是0000 0111 -7的原码是1000 0111 +0的原码是0000 0000 -0的原码是1000 0000 2. 反码 一个数如果值为正,那么反码和原码相同. 一个数如 ...
- C++中有符号/无符号数比较
原创文章,欢迎阅读,禁止转载. 在我的程序中有如下代码编译被警告了 if(list.size()>msize){...} warning C4018: '<' : signed/unsig ...
- matlab和FPGA中无符号数和有符号数的转化(转)
在FPGA 设计过程中经常会遇到关于数表示之间的转化问题,最常见的是无符号数和有符号数之间的转化问题.(1)在FPGA设计过程中,能够很直接的看出数字的位宽,但经常以无符号数的形式输出,在后继的处理中 ...
- 抢车位中的排名bug(比較使用了无符号数)
昨天把这个发在了qzone,想来还是怪怪的,还是转过来不吧,纯当发现了一个虫子,玩笑一下.只是csdn如今不能贴图,挺郁闷的,原文在http://user.qzone.qq.com/110907073 ...
- 深入理解计算机系统(2.5)------C语言中的有符号数和无符号数以及扩展和截断数字
上一篇博客我们讲解了计算机中整数的表示,包括无符号编码和补码编码,以及它们之间的互相转换,个人觉得那是非常重要的知识要点.这篇博客我们将介绍C语言中的有符号数和无符号数以及扩展和截断数字. 1.C语言 ...
随机推荐
- codeforces 278Div1 B题
虚拟参赛的时候没想到是线段树,看到很多人都过了,也蛮着急的. 首先用二分+线段树的方法更新DP[i]:它表示以A[i]为结尾可以最前到哪个位置: 再用线段树计算ans[i]:它表示当前i个A元素可以最 ...
- Xcode7.01相对于底版本的变动小结
1.在Xcode7中系统不再自动支持http请求,需要配置plist才能使用http: 2.appdelegate中的self.window不再支持直接往window上加view,必须先给window ...
- JavaScript 排序算法——快速排序
常见排序 javaScript 实现的常见排序算法有:冒泡排序.选择排序.插入排序.谢尔排序.快速排序(递归).快速排序(堆栈).归并排序.堆排序. 过程 "快速排序"的思想很简单 ...
- java 容器类大集结
这个世界是程序员的世界,归根到底是数据的世界,要统治这个世界,首先要学会征服数据. 没有最好的,只有最合适的,如何在不同的环境先选择最优的存储的结构呢?且看下文分解: 以下内容部分来自网络,参考: h ...
- python中的__init__ 、__new__、__call__等内置函数的剖析
1.__new__(cls, *args, **kwargs) 创建对象时调用,返回当前对象的一个实例;注意:这里的第一个参数是cls即class本身2.__init__(self, *args, ...
- django开发总结:
一,关于setting目录中的“DEBUG” DEBUG=False 把DEBUG从True改成False后就会出现(必需指定404和500错语页面,如上图的目录结构)找不到页面的错误.原因是DEBU ...
- 告别无止境的增删改查--Java代码生成器
转自:http://www.cnblogs.com/zhuYears/archive/2012/02/29/2373491.html 告别无止境的增删改查--Java代码生成器 有感于马上要做个比较大 ...
- PCR理解
http://blog.csdn.net/niehanzi/article/details/4450154 PCR的物理意义: PCR存在于TS包的自适应域中,如下图: PCR用来同步前端编码器和后端 ...
- ANDROID_MARS学习笔记_S02_012_ANIMATION_利用AnimationListener在动画结束时删除或添加组件
一.代码 1.xml(1)activity_main.xml <?xml version="1.0" encoding="utf-8"?> < ...
- Inline Hook NtQueryDirectoryFile
Inline Hook NtQueryDirectoryFile 首先声明这个是菜鸟—我的学习日记,不是什么高深文章,高手们慎看. 都总是发一些已经过时的文章真不好意思,几个月以来沉迷于游戏也是时候反 ...