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. 无符号数和补码

时间: 2024-10-12 21:23:28

c的基础 1. 无符号数和补码的相关文章

有符号数的加减法 和无符号数的加减法,和,系统是如何识别有符号数和无符号数的

一.有符号数的加减法 1.符号数与无符号数的人为规定性: 一个数,是有符号数还是无符号数都是人为规定的.进行二进制运算时用无符号数或是补码运算时,结果都是正确的. 10000100+00001110 若规定为无符号数,即 132+146=146D . 若规定为符号数,则为-124+14=-110,而[-110]补=10010010.解释:10000100是 -124的补码,0001110是14的补码,在机器中运算后得出的结果是[-110]的补码.机器中的有符号数的运算一般就是补码的运算. 2.补

原码、反码、补码、有符号数和无符号数运算

原码 原码就是符号位加上真值的绝对值, 即用第一位表示符号, 其余位表示值. 比如如果是8位二进制: [+1]原 = 0000 0001 [-1]原 = 1000 0001 第一位是符号位. 因为第一位是符号位, 所以8位二进制数的取值范围就是: [1111 1111 , 0111 1111] 即 [-127 , 127] 原码是人脑最容易理解和计算的表示方式. 反码 反码的表示方法是: 正数的反码是其本身 负数的反码是在其原码的基础上, 符号位不变,其余各个位取反. [+1] = [00000

C语言基础(5)-有符号数、无符号数、printf、大小端对齐

1.有符号数和无符号数 有符号数就是最高位为符号位,0代表正数,1代表负数 无符号数最高位不是符号位,而就是数的一部分而已. 1011 1111 0000 1111 1111 0000 1011 1010,如果当有符号数看待,那么他是一个负数的补码,如果当一个无符号数看待,他就是一个正数的原码 无符号数最小为0,不可能是负数 定义一个无符号的int unsigned int a; //使用unsigned int 定义了一个无符号的int变量,名字叫a short,long,long long,

C++有符号和无符号数的转换

本文转自:http://www.94cto.com/index/Article/content/id/59973.html 1.引例: 今天在做了一道关于有符号数和无符号数相互转换及其左移/右移的问题,被它们之间的转换原理和位移原理搞得头大了.真的很后悔本科的时候没有认真学习<计算机组成原理>/<计算机操作系统>等计算机基础课程.以下是我根据相关知识回顾和整理的材料,如有和某某的文章有雷同之处,请勿见怪.另外也希望看到这篇文章的同志们能够有所收获吧. 1 #include <

关于有符号数和无符号数的转换 - C/C++

转载自:http://www.94cto.com/index/Article/content/id/59973.html 1.引例: 今天在做了一道关于有符号数和无符号数相互转换及其左移/右移的问题,被它们之间的转换原理和位移原理搞得头大了.真的很后悔本科的时候没有认真学习<计算机组成原理>/<计算机操作系统>等计算机基础课程.以下是我根据相关知识回顾和整理的材料,如有和某某的文章有雷同之处,请勿见怪.另外也希望看到这篇文章的同志们能够有所收获吧. #include <cst

关于有符号数和无符号数的转换

1.引例: 今天在做了一道关于有符号数和无符号数相互转换及其左移/右移的问题,被它们之间的转换原理和位移原理搞得头大了.真的很后悔本科的时候没有认真学习<计算机组成原理>/<计算机操作系统>等计算机基础课程.以下是我根据相关知识回顾和整理的材料,如有和某某的文章有雷同之处,请勿见怪.另外也希望看到这篇文章的同志们能够有所收获吧. #include <cstdio> #include <iostream> using namespace std; int ma

有符号数和无符号数负数(转)

有符号数和无符号数负数 理解有符号数和无符号数负数在计算机中如何表示呢? 这一点,你可能听过两种不同的回答. 一种是教科书,它会告诉你:计算机用"补码"表示负数.可是有关"补码"的概念一说就得一节课,这一些我们需要在第6章中用一章的篇幅讲2进制的一切.再者,用"补码"表示负数,其实一种公式,公式的作用在于告诉你,想得问题的答案,应该如何计算.却并没有告诉你为什么用这个公式就可以和答案? 另一种是一些程序员告诉你的:用二进制数的最高位表示符号,最高

有符号数和无符号数

摘抄自:http://www.cnblogs.com/glacierh/archive/2013/07/16/3194658.html 1.      补码 在计算机中无符号数用原码表示,有符号数用补码表示.w位补码表示的值为: 最高位 也称符号位,1表示负数,0表示正数,符号位为0时,和无符号数的表示是相同的,以下是4位补码的示例: 0101 = -0*23 + 1*22 + 0*21 + 1*20 = 5 1101 = -1*23 + 1*22 + 0*21 + 1*20 = -3 w位的补

java把符号数转成无符号数

/** * @author YangQuanqing yqq 功能:将java中字节数据变成无符号数据在0--255范围内. * */ public class ConvertToUnsigned { private static short[] tempByteU = null;// 字节无符号型数据 /** *  返回无符号数 * @param a有符号字节数组 * @return字节无符号型数据 */ public static short[] toGetUnsignedByte(byte