原码、补码说明以及char和unsigned char区别--中间版

来源:互联网 发布:喀秋莎软件 课堂教学 编辑:程序博客网 时间:2024/06/05 00:59

0、

原码、补码(以及反码)这些概念都是对整数而言的,即原码、补码所表示的实际数值都是整数。而对实数(即带有小数点的数值,如5.0,,5.5)而言,是没有原码、补码(以及反码)这些概念的,即实数不能用原码、补码来表示。

原码,对应表示的是整数中的正整数部分;补码,对应表示的是整数中的负整数部分。



0,无论用原码还是补码表示,其值都是为0(即其原码值为0,其补码值也为0)。

对应能表示整数的数据类型: Int 、char等。

对应能表示实数的数据类型: float 、double等。

1、

反码:即对原码的各位取反所得的结果。

补码=反码+1。

注释:

补码的一个规定是补码的最高位要为1。故而作为补码的二进制数值的最高位必为1,最高位为1的二进制数值,我们可以视其为补码,也可以视其为原码。

通过上述这个从原码到补码的转换的方法,所得的值不一定是补码(即所得的二进制数值的最高位不一定为1),当原码(值所要存储的变量)为n位,补码(值所要存储的变量)也为n位,且原码最高位为1时,所得的n位补码的最高位一般不为1,即不是补码。上述方法能取的补码的前提是补码(值所要存储的变量)所要存储的位数要比原码(值所要存储的变量)的位数(至少)多一位,这个相当于说,补码(值所要存储的变量)所要存储的位数和原码(值所要存储的变量)的位数可以一样,但是原码(值所要存储的变量)的位数中从左向右数(连续的至少)一位是始终只能置为0的。

若我们要求的一个负整数a的补码的位数是n位的话,则该负整数a对应的正整数|a|的原码只能用n-1位(也就是说n位的补码只能表示2的n-1次方个负整数),这样就保证在原码取补码时最高位为1,而这个最高位我们就称其为符号位了。

2、

负数经过补码算法后所得的补码的最高位必然为1的。

对于一个变量的二进制值是视作原码还是视作补码,取决于变量的数据类型是有符号的(signed),还是无符号的(unsigned):

2).1

对于一个变量(如Int iR;)的二进制值的最高位为0的,

无论变量的数据类型是有符号的(signed),还是无符号的(unsigned),二进制值必然都是一个原码值。

2).2

对于一个变量的二进制值的最高位为1的,

如果该变量的数据类型是有符号的(signed),则二进制值必然都是一个补码值。
如果该变量的数据类型无符号的(unsigned,则该二进制值必然都是一个原码值。

3、

因为char类型的变量的最高位用于做符号位,不做数据位,所以char类型的变量的最高位为0的表示是正整数(也就是原码),char类型的变量的最高位为1的表示是负整数(也就是补码)。也就是说,char类型的变量的数据位为7位,故而char 表示的整数范围为-127~127。

因为unsigned char类型的变量的最高位不用于做符号位,还是做数据位,所以unsigned char类型的变量的最高位无论是为0还是为1的都是表示正整数(也就是原码)。也就是说,unsigned char类型的变量的数据位为8位,故而unsigned char 表示的整数范围为0~255。

注释:

char类型的变量的最高位为1,不是我们人为的置为1(即例如,-5的补码值为补码最高位置为1,补码数据位置为5的原码值),而是负数经过补码算法后最高位必然为1的。

所以,补码的一属性必然是有符号的(signed),不可能是无符号的(unsigned)。

4、

补码和补码,补码和原码间的运算前提是要先给补码原码确定存放其的变量的数据类型,只有数据类型相同的补码和补码,补码和原码间才可以直接运算:

原码、补码(以及反码)这些概念都是有一个修饰条件的,即几位的原码、几位的补码。例如,8bit原码,8bit补码,32bit的原码32bit补码原码、补码(以及反码)这些概念的另一个修饰条件是该原码、补码是有符号的(signed),还是无符号的(unsigned)。其实,确定这两点相当于确定一个具体数据类型(的变量)来存放这些原码、补码值。例如,unsigned char类型的变量,只可以用于存储8bit无符号的(unsigned原码,而 char类型的变量,可以用于存储8bit的有符号的(signed原码(符号的,意味着其数据位为7bit)和8bit的有符号的(signed)补码。

只有位数以及有无符号这两个属性都相同的补码和补码,补码和原码间才可以(直接)进行加减乘除的运算(而不必转码再运算)

例如,8bit补码0XFF(即-1)加上8bit补码0XFF(即-1)0XFF+0XFF=0X1FE,由于所得的结果即0X1FE,CPU依旧用8bit存储空间(寄存器变量)存储,所以在该存储空间里的值为0XFE,即-2.即运算结果是正确的。

再例如,8bit补码0XFF(即-1)加上8bit原码0X01(即1)0XFF+0X01=0X100,由于所得的结果即0X1FE,CPU依旧用8bit的存储空间(寄存器变量)存储,所以在该存储空间里的值为0X00,即0.即运算结果是正确的。

所以,char cty=-1,ctu=-1;int  itt=cty+ctu;中cty+ctu所得的中间运算结果的数据类型为char,即int  itt=cty+ctu相当于char temp=cty+ctu;int  itt= (int) temp;这里有个数据类型转换。

注释:

原码与原码间的可以(直接)进行加减乘除的运算,即使两者间的位数以及有无符号这两个属性不都相同。


数据类型不相同的补码和补码,补码和原码间进行加减乘除的运算前,会将补码、原码的数据类型(尽量)统一成其中有符号的占用字节大的数据类型:

1、signed char的8bit补码、unsigned char的8bit原码间运算,则数据类型统一转换成signed int(即32bit)的补码和原码

2、signed int的补码、signed char(或是unsigned char)的原码间运算,则数据类型统一转换成signed int的补码和原码


unsigned char的原码转换成signed char的原码时可能会出现数据丢失,

unsigned char的原码转换成signed int的原码时则不会出现数据丢失,

原因都是因为大范围到小范围的数据值表示可能会丢失数据。

5、

char(即signed char)和unsigned char区别不在于 

哪个能表示的字符多,哪个能表示的字符少,两者能表示的字符都是一样的。

因为首先无论以何种字符集来编码一字符串,该字符串的编码值都可以存储在任何数据类型的变量上(前提是要将该字符串的编码值原原本本的复制到该变量上),再又char(即signed char)和unsigned char两者都是根据当前(Windows)操作系统中的系统区域设置里选择的字符集来读取解释显示存储在其变量上的编码值的,所以两者能表示的字符都是一样的。(详见:windowsAPI函数实际用它的ASCII版本函数还是用它的宽字符版本函数取决于啥  

char(即signed char)和unsigned char区别在于

当(在源代码文件中)将两者的变量按照整数类型输出(如unsigned char str=‘标’;printf(“%d”,str);)或是转换为整数类型(如unsigned char str=‘标’;Int  is=str;//这是隐式类型转换,而Int  is=Int  )str是强制类型转换)时,编译器会将数据类型为unsigned char的变量的二进制值视为8bit无符号的(unsigned原码,而将数据类型为char的且其二进制值的最高位为0的变量的二进制值视为8bit的有符号的(signed)原码,数据类型为char的且其二进制值的最高位为1的变量的二进制值视为8bit的有符号的(signed)补码。


6、

8bit的有符号的(signed)补码如何转换成32bit的有符号的(signed)补码(例如,char cc=’标‘;int iR=cc;)?

就是在32bit的变量的前24位都置为1.因为8bit的补码只有7位是数据位(故所能表示数据范围为-127~-1),最高位为符号位,值为1,所以-127~-1的负整数转换为8bit的补码时从该负整数对应的正整数的原码所取的反码的最高位必然为1,加1得补码时又不会对反码的最高位进1(除了0的补码会的之外),即-127~-1的负整数转换为8bit的补码的最高位为1.而-127~-1的负整数转换为32bit的补码时也先从该负整数对应的正整数的原码取反码,该反码从右向前数的第八位必然为1(因为该位等价于转换为8bit的补码时从该负整数对应的正整数的原码所取的反码的最高位),该反码从左开始数的前24位为1,加1时,我们知道-127~-1的负整数转换为8bit的补码时其反码加1得补码时不会对反码的最高位进1,故而这里32bit的反码从左开始数的前24位的值不会改变还是各位为1。所以,8bit的补码转换成32bit的补码只要在32bit的变量的前24位都置为1而后8位依旧为8bit的补码的值即可。

注释:

在如字符集GBK中,英文字符的编码值(的一个字节)都是小于127,即一个英文字符的编码值中的一个字节的最高位为0,中文字符的编码值中的一个字节都是大于127,即一个中文字符的编码值中的一个字节的最高位为1,这个才是补码。



7、

%x  默认输出的数据类型是unsigned int。

证明如下:

char tt=-2;

printf(“%X”,tt);

结果:

FFFFFFFE

再者,输出的是-2的补码值,也就是说存在一个数据类型转换:unsigned int   _%X=tt,故而能将-2的补码值按原码形式输出。(int)-2的补码(即FFFFFFFE)的低8位(即FE)存入变量tt中,由于变量tt的数据类型是char,而变量tt当前值为FE,即变量tt当前最高位为1,所以变量tt当前值FE被解释为char类型的8bit补码而转换成signedint类型的32bit补码存放在int gg;中。输出时,gg中存放的值又按照unsigned int 数据类型(即32bit原码)输出的。这是因为存在一个共用体union{int gg;unsigned int dd;},变量tt当前值FE被解释为char类型的8bit补码而转换成int类型的32bit补码存放在共用体中的 gg上,输出时按照共用体中的dd输出。用另一种方式,也可以解释为:unsigned int   _%X=*((unsigned intunsigned int *)&((int)tt))   。

故而%x 输出的数据不会是负整数,只能是正整数。

附加:

unsigned char tt=-2;

printf(“%X”,tt);

结果:

FE

说明:

(int)-2的补码(即FFFFFFFE)的低8位(即FE)存入变量tt中,由于变量tt的数据类型是unsigned char,所以变量tt当前值FE被解释为unsigned char类型的8bit原码而转换成signed int类型的32bit原码存放在int gg;中。输出时,gg中存放的值又按照unsigned int 数据类型(即32bit原码)输出的。这是因为存在一个共用体union{int gg;unsigned int dd;},变量tt当前值FE被解释为unsigned char类型的8bit原码而转换成signed int类型的32bit原码存放在共用体中的 gg上,输出时按照共用体中的dd输出。用另一种方式,也可以解释为:unsigned int   _%X=*((unsigned intunsigned int *)&((int)tt))  。




%d默认输出的数据类型是 int。

证明如下:

int  z=-(2^31-1);

printf(“%d  \n”,z);



结果:

-(2^31-1)             //-(2的31次方-1)

首先,能存储-(2^31-1)这样一个数值需要至少4个字节,而结果既然能正确显示-(2^31-1)这个数值,说明(至少)占用4个字节。

再者,输出的-(2^31-1)是一个负整数,说明其数据类型是有符号的(signed )。

所以,%d默认输出的数据类型是 int。



0 0